Added gdk_text/string_extents() - too calculate all the metrics at once of
authorOwen Taylor <otaylor@redhat.com>
Wed, 12 Aug 1998 16:49:13 +0000 (16:49 +0000)
committerOwen Taylor <otaylor@src.gnome.org>
Wed, 12 Aug 1998 16:49:13 +0000 (16:49 +0000)
Tue Jul 21 12:42:01 1998  Owen Taylor  <otaylor@redhat.com>

* gdk/gdk.h gdk/gdkfont.c: Added gdk_text/string_extents() -
  too calculate all the metrics at once of a string, including
  things which weren't calculated before.

* gtk/Makefile.am gtk/gtk.h gtk/gtktearoffmenu.[ch]: New
  MenuItem type, that when put as the first thing in a
  menu, makes the menu tearoff. Currently drawn as a
  dashed line.

* gtk/gtkmenuitem.h gtk/gtkcheckmenuitem.c: Added a flag
  "hide_on_activate" to the MenuItem class structure to allow
  check and radio buttons to be changed with <Space> without
  hiding the menu.

* gtk/gtkaccellabel.[ch]: Added new capabilities to set
  a underline_group and underline_mods for the label -
  accelerators added in the underline group matching
  underline_mods will be displayed as an underline character.

  This doesn't work - Save As needs to be underlined
  as Save _As.

* gtk/gtkitemfactory.c:
  - Create a AccelGroup for each MenuShell we create.
  - If an '&' appears before a  character 'c' in the path,
    then make 'c' an accelerator in the menu's accel group,
    and if the menuitem is menubar <alt>C an accelerator
    in the itemfactory's accel group.

* gtk/gtklabel.[ch]: Add support for a pattern arg -
          which is a string. If an '_' appears in this string,
  the corresponding position in the label is underlined.

          Add gtk_label_parse_uline() convenience function which
  takes a string with embedded underlines, sets the
  pattern and label, and returns the accelerator keyval.

* gtk/gtkmenu.[ch]: Make menus no longer a toplevel widget.
  Instead, they create a GtkWindow and add themselves
  to that. (When torn off, another new feature, they
  create another GtkWindow to hold the torn off menu)

  New function gtk_menu_set_tearoff_state()

* gtk/gtkenums.h gtk/gtkmenushell.[ch] gtk/gtkenums.h:
  Added action signals  for keyboard navigation of menus.

* gtk/gtkmenushell.c: Key press handler which activates
          bindings for navigation, and accelerators, for handling
  underline accelerators. Exported functions to select
  and activate menu items in a menushell.

* gtk/testgtk.c: Added a new "Item Factory" test which
  tests GtkItemFactory and the new keyboard navigation
  of menus.

37 files changed:
ChangeLog
ChangeLog.pre-2-0
ChangeLog.pre-2-10
ChangeLog.pre-2-2
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
docs/widget_system.txt
gdk/gdk.h
gdk/gdkfont.c
gdk/x11/gdkfont-x11.c
gtk/Makefile.am
gtk/gtk.defs
gtk/gtk.h
gtk/gtkcheckmenuitem.c
gtk/gtkenums.h
gtk/gtkitem.c
gtk/gtkitemfactory.c
gtk/gtklabel.c
gtk/gtklabel.h
gtk/gtkmenu.c
gtk/gtkmenu.h
gtk/gtkmenubar.c
gtk/gtkmenuitem.c
gtk/gtkmenuitem.h
gtk/gtkmenushell.c
gtk/gtkmenushell.h
gtk/gtktearoffmenuitem.c [new file with mode: 0644]
gtk/gtktearoffmenuitem.h [new file with mode: 0644]
gtk/gtktoolbar.c
gtk/gtktypebuiltins.h
gtk/gtktypebuiltins_evals.c
gtk/gtktypebuiltins_ids.c
gtk/gtktypebuiltins_vars.c
gtk/gtkwidget.c
gtk/testgtk.c
tests/testgtk.c

index 6c931162c52f222a080ba11782c0d7d725199ca4..268879630aed2b11137ebf9f5c3335d513dcd019 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,61 @@
+Tue Jul 21 12:42:01 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gdk/gdk.h gdk/gdkfont.c: Added gdk_text/string_extents() - 
+         too calculate all the metrics at once of a string, including
+         things which weren't calculated before.
+
+       * gtk/Makefile.am gtk/gtk.h gtk/gtktearoffmenu.[ch]: New
+         MenuItem type, that when put as the first thing in a
+         menu, makes the menu tearoff. Currently drawn as a
+         dashed line.
+
+       * gtk/gtkmenuitem.h gtk/gtkcheckmenuitem.c: Added a flag
+         "hide_on_activate" to the MenuItem class structure to allow
+         check and radio buttons to be changed with <Space> without
+         hiding the menu.
+
+       * gtk/gtkaccellabel.[ch]: Added new capabilities to set
+         a underline_group and underline_mods for the label - 
+         accelerators added in the underline group matching
+         underline_mods will be displayed as an underline character.
+
+         This doesn't work - Save As needs to be underlined 
+         as Save _As.
+        
+       * gtk/gtkitemfactory.c: 
+         - Create a AccelGroup for each MenuShell we create.
+         - If an '&' appears before a  character 'c' in the path,
+           then make 'c' an accelerator in the menu's accel group,
+           and if the menuitem is menubar <alt>C an accelerator 
+           in the itemfactory's accel group.
+
+       * gtk/gtklabel.[ch]: Add support for a pattern arg - 
+          which is a string. If an '_' appears in this string,
+         the corresponding position in the label is underlined.
+
+          Add gtk_label_parse_uline() convenience function which
+         takes a string with embedded underlines, sets the
+         pattern and label, and returns the accelerator keyval.
+
+       * gtk/gtkmenu.[ch]: Make menus no longer a toplevel widget.
+         Instead, they create a GtkWindow and add themselves
+         to that. (When torn off, another new feature, they
+         create another GtkWindow to hold the torn off menu)
+
+         New function gtk_menu_set_tearoff_state()
+       
+       * gtk/gtkenums.h gtk/gtkmenushell.[ch] gtk/gtkenums.h: 
+         Added action signals  for keyboard navigation of menus.
+
+       * gtk/gtkmenushell.c: Key press handler which activates
+          bindings for navigation, and accelerators, for handling
+         underline accelerators. Exported functions to select 
+         and activate menu items in a menushell.
+
+       * gtk/testgtk.c: Added a new "Item Factory" test which
+         tests GtkItemFactory and the new keyboard navigation
+         of menus.
+
 Tue Aug 11 20:52:58 1998  Tim Janik  <timj@gtk.org>
 
        * gtk/gtktypeutils.c (gtk_type_class_init): relookup nodes after
@@ -68,6 +126,7 @@ Thu Aug  6 22:37:21 1998  Stefan Jeske  <stefan@gtk.org>
        * gtk/gtkclist.c: Renamed my_merge and my_mergesort to
        gtk_clist_merge and gtk_clist_mergesort. :)
 
+>>>>>>> 1.579
 1998-08-06  Martin Baulig  <martin@home-of-linux.org>
 
        * gtk/gtkclist.c: Renamed `merge' -> `my_merge' and
@@ -391,6 +450,15 @@ Wed Jul 15 17:44:47 1998  Owen Taylor  <otaylor@redhat.com>
        * Fix up line start cache when splitting a property
        during an insert.
 
+Wed Jul 15 21:15:52 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkwidget.c: restauration => restoration
+
+Thu Jul 16 20:11:36 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gdk/gdkxid.c (gdk_xid_table_lookup): If xid_ht
+       hasn't been created yet, don't do the lookup.
+
 1998-07-20  Raph Levien  <raph@acm.org>
 
        * gdk/gdkrgb.c: More cleanups in the config process.
index 6c931162c52f222a080ba11782c0d7d725199ca4..268879630aed2b11137ebf9f5c3335d513dcd019 100644 (file)
@@ -1,3 +1,61 @@
+Tue Jul 21 12:42:01 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gdk/gdk.h gdk/gdkfont.c: Added gdk_text/string_extents() - 
+         too calculate all the metrics at once of a string, including
+         things which weren't calculated before.
+
+       * gtk/Makefile.am gtk/gtk.h gtk/gtktearoffmenu.[ch]: New
+         MenuItem type, that when put as the first thing in a
+         menu, makes the menu tearoff. Currently drawn as a
+         dashed line.
+
+       * gtk/gtkmenuitem.h gtk/gtkcheckmenuitem.c: Added a flag
+         "hide_on_activate" to the MenuItem class structure to allow
+         check and radio buttons to be changed with <Space> without
+         hiding the menu.
+
+       * gtk/gtkaccellabel.[ch]: Added new capabilities to set
+         a underline_group and underline_mods for the label - 
+         accelerators added in the underline group matching
+         underline_mods will be displayed as an underline character.
+
+         This doesn't work - Save As needs to be underlined 
+         as Save _As.
+        
+       * gtk/gtkitemfactory.c: 
+         - Create a AccelGroup for each MenuShell we create.
+         - If an '&' appears before a  character 'c' in the path,
+           then make 'c' an accelerator in the menu's accel group,
+           and if the menuitem is menubar <alt>C an accelerator 
+           in the itemfactory's accel group.
+
+       * gtk/gtklabel.[ch]: Add support for a pattern arg - 
+          which is a string. If an '_' appears in this string,
+         the corresponding position in the label is underlined.
+
+          Add gtk_label_parse_uline() convenience function which
+         takes a string with embedded underlines, sets the
+         pattern and label, and returns the accelerator keyval.
+
+       * gtk/gtkmenu.[ch]: Make menus no longer a toplevel widget.
+         Instead, they create a GtkWindow and add themselves
+         to that. (When torn off, another new feature, they
+         create another GtkWindow to hold the torn off menu)
+
+         New function gtk_menu_set_tearoff_state()
+       
+       * gtk/gtkenums.h gtk/gtkmenushell.[ch] gtk/gtkenums.h: 
+         Added action signals  for keyboard navigation of menus.
+
+       * gtk/gtkmenushell.c: Key press handler which activates
+          bindings for navigation, and accelerators, for handling
+         underline accelerators. Exported functions to select 
+         and activate menu items in a menushell.
+
+       * gtk/testgtk.c: Added a new "Item Factory" test which
+         tests GtkItemFactory and the new keyboard navigation
+         of menus.
+
 Tue Aug 11 20:52:58 1998  Tim Janik  <timj@gtk.org>
 
        * gtk/gtktypeutils.c (gtk_type_class_init): relookup nodes after
@@ -68,6 +126,7 @@ Thu Aug  6 22:37:21 1998  Stefan Jeske  <stefan@gtk.org>
        * gtk/gtkclist.c: Renamed my_merge and my_mergesort to
        gtk_clist_merge and gtk_clist_mergesort. :)
 
+>>>>>>> 1.579
 1998-08-06  Martin Baulig  <martin@home-of-linux.org>
 
        * gtk/gtkclist.c: Renamed `merge' -> `my_merge' and
@@ -391,6 +450,15 @@ Wed Jul 15 17:44:47 1998  Owen Taylor  <otaylor@redhat.com>
        * Fix up line start cache when splitting a property
        during an insert.
 
+Wed Jul 15 21:15:52 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkwidget.c: restauration => restoration
+
+Thu Jul 16 20:11:36 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gdk/gdkxid.c (gdk_xid_table_lookup): If xid_ht
+       hasn't been created yet, don't do the lookup.
+
 1998-07-20  Raph Levien  <raph@acm.org>
 
        * gdk/gdkrgb.c: More cleanups in the config process.
index 6c931162c52f222a080ba11782c0d7d725199ca4..268879630aed2b11137ebf9f5c3335d513dcd019 100644 (file)
@@ -1,3 +1,61 @@
+Tue Jul 21 12:42:01 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gdk/gdk.h gdk/gdkfont.c: Added gdk_text/string_extents() - 
+         too calculate all the metrics at once of a string, including
+         things which weren't calculated before.
+
+       * gtk/Makefile.am gtk/gtk.h gtk/gtktearoffmenu.[ch]: New
+         MenuItem type, that when put as the first thing in a
+         menu, makes the menu tearoff. Currently drawn as a
+         dashed line.
+
+       * gtk/gtkmenuitem.h gtk/gtkcheckmenuitem.c: Added a flag
+         "hide_on_activate" to the MenuItem class structure to allow
+         check and radio buttons to be changed with <Space> without
+         hiding the menu.
+
+       * gtk/gtkaccellabel.[ch]: Added new capabilities to set
+         a underline_group and underline_mods for the label - 
+         accelerators added in the underline group matching
+         underline_mods will be displayed as an underline character.
+
+         This doesn't work - Save As needs to be underlined 
+         as Save _As.
+        
+       * gtk/gtkitemfactory.c: 
+         - Create a AccelGroup for each MenuShell we create.
+         - If an '&' appears before a  character 'c' in the path,
+           then make 'c' an accelerator in the menu's accel group,
+           and if the menuitem is menubar <alt>C an accelerator 
+           in the itemfactory's accel group.
+
+       * gtk/gtklabel.[ch]: Add support for a pattern arg - 
+          which is a string. If an '_' appears in this string,
+         the corresponding position in the label is underlined.
+
+          Add gtk_label_parse_uline() convenience function which
+         takes a string with embedded underlines, sets the
+         pattern and label, and returns the accelerator keyval.
+
+       * gtk/gtkmenu.[ch]: Make menus no longer a toplevel widget.
+         Instead, they create a GtkWindow and add themselves
+         to that. (When torn off, another new feature, they
+         create another GtkWindow to hold the torn off menu)
+
+         New function gtk_menu_set_tearoff_state()
+       
+       * gtk/gtkenums.h gtk/gtkmenushell.[ch] gtk/gtkenums.h: 
+         Added action signals  for keyboard navigation of menus.
+
+       * gtk/gtkmenushell.c: Key press handler which activates
+          bindings for navigation, and accelerators, for handling
+         underline accelerators. Exported functions to select 
+         and activate menu items in a menushell.
+
+       * gtk/testgtk.c: Added a new "Item Factory" test which
+         tests GtkItemFactory and the new keyboard navigation
+         of menus.
+
 Tue Aug 11 20:52:58 1998  Tim Janik  <timj@gtk.org>
 
        * gtk/gtktypeutils.c (gtk_type_class_init): relookup nodes after
@@ -68,6 +126,7 @@ Thu Aug  6 22:37:21 1998  Stefan Jeske  <stefan@gtk.org>
        * gtk/gtkclist.c: Renamed my_merge and my_mergesort to
        gtk_clist_merge and gtk_clist_mergesort. :)
 
+>>>>>>> 1.579
 1998-08-06  Martin Baulig  <martin@home-of-linux.org>
 
        * gtk/gtkclist.c: Renamed `merge' -> `my_merge' and
@@ -391,6 +450,15 @@ Wed Jul 15 17:44:47 1998  Owen Taylor  <otaylor@redhat.com>
        * Fix up line start cache when splitting a property
        during an insert.
 
+Wed Jul 15 21:15:52 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkwidget.c: restauration => restoration
+
+Thu Jul 16 20:11:36 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gdk/gdkxid.c (gdk_xid_table_lookup): If xid_ht
+       hasn't been created yet, don't do the lookup.
+
 1998-07-20  Raph Levien  <raph@acm.org>
 
        * gdk/gdkrgb.c: More cleanups in the config process.
index 6c931162c52f222a080ba11782c0d7d725199ca4..268879630aed2b11137ebf9f5c3335d513dcd019 100644 (file)
@@ -1,3 +1,61 @@
+Tue Jul 21 12:42:01 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gdk/gdk.h gdk/gdkfont.c: Added gdk_text/string_extents() - 
+         too calculate all the metrics at once of a string, including
+         things which weren't calculated before.
+
+       * gtk/Makefile.am gtk/gtk.h gtk/gtktearoffmenu.[ch]: New
+         MenuItem type, that when put as the first thing in a
+         menu, makes the menu tearoff. Currently drawn as a
+         dashed line.
+
+       * gtk/gtkmenuitem.h gtk/gtkcheckmenuitem.c: Added a flag
+         "hide_on_activate" to the MenuItem class structure to allow
+         check and radio buttons to be changed with <Space> without
+         hiding the menu.
+
+       * gtk/gtkaccellabel.[ch]: Added new capabilities to set
+         a underline_group and underline_mods for the label - 
+         accelerators added in the underline group matching
+         underline_mods will be displayed as an underline character.
+
+         This doesn't work - Save As needs to be underlined 
+         as Save _As.
+        
+       * gtk/gtkitemfactory.c: 
+         - Create a AccelGroup for each MenuShell we create.
+         - If an '&' appears before a  character 'c' in the path,
+           then make 'c' an accelerator in the menu's accel group,
+           and if the menuitem is menubar <alt>C an accelerator 
+           in the itemfactory's accel group.
+
+       * gtk/gtklabel.[ch]: Add support for a pattern arg - 
+          which is a string. If an '_' appears in this string,
+         the corresponding position in the label is underlined.
+
+          Add gtk_label_parse_uline() convenience function which
+         takes a string with embedded underlines, sets the
+         pattern and label, and returns the accelerator keyval.
+
+       * gtk/gtkmenu.[ch]: Make menus no longer a toplevel widget.
+         Instead, they create a GtkWindow and add themselves
+         to that. (When torn off, another new feature, they
+         create another GtkWindow to hold the torn off menu)
+
+         New function gtk_menu_set_tearoff_state()
+       
+       * gtk/gtkenums.h gtk/gtkmenushell.[ch] gtk/gtkenums.h: 
+         Added action signals  for keyboard navigation of menus.
+
+       * gtk/gtkmenushell.c: Key press handler which activates
+          bindings for navigation, and accelerators, for handling
+         underline accelerators. Exported functions to select 
+         and activate menu items in a menushell.
+
+       * gtk/testgtk.c: Added a new "Item Factory" test which
+         tests GtkItemFactory and the new keyboard navigation
+         of menus.
+
 Tue Aug 11 20:52:58 1998  Tim Janik  <timj@gtk.org>
 
        * gtk/gtktypeutils.c (gtk_type_class_init): relookup nodes after
@@ -68,6 +126,7 @@ Thu Aug  6 22:37:21 1998  Stefan Jeske  <stefan@gtk.org>
        * gtk/gtkclist.c: Renamed my_merge and my_mergesort to
        gtk_clist_merge and gtk_clist_mergesort. :)
 
+>>>>>>> 1.579
 1998-08-06  Martin Baulig  <martin@home-of-linux.org>
 
        * gtk/gtkclist.c: Renamed `merge' -> `my_merge' and
@@ -391,6 +450,15 @@ Wed Jul 15 17:44:47 1998  Owen Taylor  <otaylor@redhat.com>
        * Fix up line start cache when splitting a property
        during an insert.
 
+Wed Jul 15 21:15:52 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkwidget.c: restauration => restoration
+
+Thu Jul 16 20:11:36 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gdk/gdkxid.c (gdk_xid_table_lookup): If xid_ht
+       hasn't been created yet, don't do the lookup.
+
 1998-07-20  Raph Levien  <raph@acm.org>
 
        * gdk/gdkrgb.c: More cleanups in the config process.
index 6c931162c52f222a080ba11782c0d7d725199ca4..268879630aed2b11137ebf9f5c3335d513dcd019 100644 (file)
@@ -1,3 +1,61 @@
+Tue Jul 21 12:42:01 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gdk/gdk.h gdk/gdkfont.c: Added gdk_text/string_extents() - 
+         too calculate all the metrics at once of a string, including
+         things which weren't calculated before.
+
+       * gtk/Makefile.am gtk/gtk.h gtk/gtktearoffmenu.[ch]: New
+         MenuItem type, that when put as the first thing in a
+         menu, makes the menu tearoff. Currently drawn as a
+         dashed line.
+
+       * gtk/gtkmenuitem.h gtk/gtkcheckmenuitem.c: Added a flag
+         "hide_on_activate" to the MenuItem class structure to allow
+         check and radio buttons to be changed with <Space> without
+         hiding the menu.
+
+       * gtk/gtkaccellabel.[ch]: Added new capabilities to set
+         a underline_group and underline_mods for the label - 
+         accelerators added in the underline group matching
+         underline_mods will be displayed as an underline character.
+
+         This doesn't work - Save As needs to be underlined 
+         as Save _As.
+        
+       * gtk/gtkitemfactory.c: 
+         - Create a AccelGroup for each MenuShell we create.
+         - If an '&' appears before a  character 'c' in the path,
+           then make 'c' an accelerator in the menu's accel group,
+           and if the menuitem is menubar <alt>C an accelerator 
+           in the itemfactory's accel group.
+
+       * gtk/gtklabel.[ch]: Add support for a pattern arg - 
+          which is a string. If an '_' appears in this string,
+         the corresponding position in the label is underlined.
+
+          Add gtk_label_parse_uline() convenience function which
+         takes a string with embedded underlines, sets the
+         pattern and label, and returns the accelerator keyval.
+
+       * gtk/gtkmenu.[ch]: Make menus no longer a toplevel widget.
+         Instead, they create a GtkWindow and add themselves
+         to that. (When torn off, another new feature, they
+         create another GtkWindow to hold the torn off menu)
+
+         New function gtk_menu_set_tearoff_state()
+       
+       * gtk/gtkenums.h gtk/gtkmenushell.[ch] gtk/gtkenums.h: 
+         Added action signals  for keyboard navigation of menus.
+
+       * gtk/gtkmenushell.c: Key press handler which activates
+          bindings for navigation, and accelerators, for handling
+         underline accelerators. Exported functions to select 
+         and activate menu items in a menushell.
+
+       * gtk/testgtk.c: Added a new "Item Factory" test which
+         tests GtkItemFactory and the new keyboard navigation
+         of menus.
+
 Tue Aug 11 20:52:58 1998  Tim Janik  <timj@gtk.org>
 
        * gtk/gtktypeutils.c (gtk_type_class_init): relookup nodes after
@@ -68,6 +126,7 @@ Thu Aug  6 22:37:21 1998  Stefan Jeske  <stefan@gtk.org>
        * gtk/gtkclist.c: Renamed my_merge and my_mergesort to
        gtk_clist_merge and gtk_clist_mergesort. :)
 
+>>>>>>> 1.579
 1998-08-06  Martin Baulig  <martin@home-of-linux.org>
 
        * gtk/gtkclist.c: Renamed `merge' -> `my_merge' and
@@ -391,6 +450,15 @@ Wed Jul 15 17:44:47 1998  Owen Taylor  <otaylor@redhat.com>
        * Fix up line start cache when splitting a property
        during an insert.
 
+Wed Jul 15 21:15:52 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkwidget.c: restauration => restoration
+
+Thu Jul 16 20:11:36 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gdk/gdkxid.c (gdk_xid_table_lookup): If xid_ht
+       hasn't been created yet, don't do the lookup.
+
 1998-07-20  Raph Levien  <raph@acm.org>
 
        * gdk/gdkrgb.c: More cleanups in the config process.
index 6c931162c52f222a080ba11782c0d7d725199ca4..268879630aed2b11137ebf9f5c3335d513dcd019 100644 (file)
@@ -1,3 +1,61 @@
+Tue Jul 21 12:42:01 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gdk/gdk.h gdk/gdkfont.c: Added gdk_text/string_extents() - 
+         too calculate all the metrics at once of a string, including
+         things which weren't calculated before.
+
+       * gtk/Makefile.am gtk/gtk.h gtk/gtktearoffmenu.[ch]: New
+         MenuItem type, that when put as the first thing in a
+         menu, makes the menu tearoff. Currently drawn as a
+         dashed line.
+
+       * gtk/gtkmenuitem.h gtk/gtkcheckmenuitem.c: Added a flag
+         "hide_on_activate" to the MenuItem class structure to allow
+         check and radio buttons to be changed with <Space> without
+         hiding the menu.
+
+       * gtk/gtkaccellabel.[ch]: Added new capabilities to set
+         a underline_group and underline_mods for the label - 
+         accelerators added in the underline group matching
+         underline_mods will be displayed as an underline character.
+
+         This doesn't work - Save As needs to be underlined 
+         as Save _As.
+        
+       * gtk/gtkitemfactory.c: 
+         - Create a AccelGroup for each MenuShell we create.
+         - If an '&' appears before a  character 'c' in the path,
+           then make 'c' an accelerator in the menu's accel group,
+           and if the menuitem is menubar <alt>C an accelerator 
+           in the itemfactory's accel group.
+
+       * gtk/gtklabel.[ch]: Add support for a pattern arg - 
+          which is a string. If an '_' appears in this string,
+         the corresponding position in the label is underlined.
+
+          Add gtk_label_parse_uline() convenience function which
+         takes a string with embedded underlines, sets the
+         pattern and label, and returns the accelerator keyval.
+
+       * gtk/gtkmenu.[ch]: Make menus no longer a toplevel widget.
+         Instead, they create a GtkWindow and add themselves
+         to that. (When torn off, another new feature, they
+         create another GtkWindow to hold the torn off menu)
+
+         New function gtk_menu_set_tearoff_state()
+       
+       * gtk/gtkenums.h gtk/gtkmenushell.[ch] gtk/gtkenums.h: 
+         Added action signals  for keyboard navigation of menus.
+
+       * gtk/gtkmenushell.c: Key press handler which activates
+          bindings for navigation, and accelerators, for handling
+         underline accelerators. Exported functions to select 
+         and activate menu items in a menushell.
+
+       * gtk/testgtk.c: Added a new "Item Factory" test which
+         tests GtkItemFactory and the new keyboard navigation
+         of menus.
+
 Tue Aug 11 20:52:58 1998  Tim Janik  <timj@gtk.org>
 
        * gtk/gtktypeutils.c (gtk_type_class_init): relookup nodes after
@@ -68,6 +126,7 @@ Thu Aug  6 22:37:21 1998  Stefan Jeske  <stefan@gtk.org>
        * gtk/gtkclist.c: Renamed my_merge and my_mergesort to
        gtk_clist_merge and gtk_clist_mergesort. :)
 
+>>>>>>> 1.579
 1998-08-06  Martin Baulig  <martin@home-of-linux.org>
 
        * gtk/gtkclist.c: Renamed `merge' -> `my_merge' and
@@ -391,6 +450,15 @@ Wed Jul 15 17:44:47 1998  Owen Taylor  <otaylor@redhat.com>
        * Fix up line start cache when splitting a property
        during an insert.
 
+Wed Jul 15 21:15:52 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkwidget.c: restauration => restoration
+
+Thu Jul 16 20:11:36 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gdk/gdkxid.c (gdk_xid_table_lookup): If xid_ht
+       hasn't been created yet, don't do the lookup.
+
 1998-07-20  Raph Levien  <raph@acm.org>
 
        * gdk/gdkrgb.c: More cleanups in the config process.
index 6c931162c52f222a080ba11782c0d7d725199ca4..268879630aed2b11137ebf9f5c3335d513dcd019 100644 (file)
@@ -1,3 +1,61 @@
+Tue Jul 21 12:42:01 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gdk/gdk.h gdk/gdkfont.c: Added gdk_text/string_extents() - 
+         too calculate all the metrics at once of a string, including
+         things which weren't calculated before.
+
+       * gtk/Makefile.am gtk/gtk.h gtk/gtktearoffmenu.[ch]: New
+         MenuItem type, that when put as the first thing in a
+         menu, makes the menu tearoff. Currently drawn as a
+         dashed line.
+
+       * gtk/gtkmenuitem.h gtk/gtkcheckmenuitem.c: Added a flag
+         "hide_on_activate" to the MenuItem class structure to allow
+         check and radio buttons to be changed with <Space> without
+         hiding the menu.
+
+       * gtk/gtkaccellabel.[ch]: Added new capabilities to set
+         a underline_group and underline_mods for the label - 
+         accelerators added in the underline group matching
+         underline_mods will be displayed as an underline character.
+
+         This doesn't work - Save As needs to be underlined 
+         as Save _As.
+        
+       * gtk/gtkitemfactory.c: 
+         - Create a AccelGroup for each MenuShell we create.
+         - If an '&' appears before a  character 'c' in the path,
+           then make 'c' an accelerator in the menu's accel group,
+           and if the menuitem is menubar <alt>C an accelerator 
+           in the itemfactory's accel group.
+
+       * gtk/gtklabel.[ch]: Add support for a pattern arg - 
+          which is a string. If an '_' appears in this string,
+         the corresponding position in the label is underlined.
+
+          Add gtk_label_parse_uline() convenience function which
+         takes a string with embedded underlines, sets the
+         pattern and label, and returns the accelerator keyval.
+
+       * gtk/gtkmenu.[ch]: Make menus no longer a toplevel widget.
+         Instead, they create a GtkWindow and add themselves
+         to that. (When torn off, another new feature, they
+         create another GtkWindow to hold the torn off menu)
+
+         New function gtk_menu_set_tearoff_state()
+       
+       * gtk/gtkenums.h gtk/gtkmenushell.[ch] gtk/gtkenums.h: 
+         Added action signals  for keyboard navigation of menus.
+
+       * gtk/gtkmenushell.c: Key press handler which activates
+          bindings for navigation, and accelerators, for handling
+         underline accelerators. Exported functions to select 
+         and activate menu items in a menushell.
+
+       * gtk/testgtk.c: Added a new "Item Factory" test which
+         tests GtkItemFactory and the new keyboard navigation
+         of menus.
+
 Tue Aug 11 20:52:58 1998  Tim Janik  <timj@gtk.org>
 
        * gtk/gtktypeutils.c (gtk_type_class_init): relookup nodes after
@@ -68,6 +126,7 @@ Thu Aug  6 22:37:21 1998  Stefan Jeske  <stefan@gtk.org>
        * gtk/gtkclist.c: Renamed my_merge and my_mergesort to
        gtk_clist_merge and gtk_clist_mergesort. :)
 
+>>>>>>> 1.579
 1998-08-06  Martin Baulig  <martin@home-of-linux.org>
 
        * gtk/gtkclist.c: Renamed `merge' -> `my_merge' and
@@ -391,6 +450,15 @@ Wed Jul 15 17:44:47 1998  Owen Taylor  <otaylor@redhat.com>
        * Fix up line start cache when splitting a property
        during an insert.
 
+Wed Jul 15 21:15:52 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gtk/gtkwidget.c: restauration => restoration
+
+Thu Jul 16 20:11:36 1998  Owen Taylor  <otaylor@redhat.com>
+
+       * gdk/gdkxid.c (gdk_xid_table_lookup): If xid_ht
+       hasn't been created yet, don't do the lookup.
+
 1998-07-20  Raph Levien  <raph@acm.org>
 
        * gdk/gdkrgb.c: More cleanups in the config process.
index e5f032867198ccedb2735d4e36b493ddaa5cec2d..07a5532db6b80f315e84a03b192e2dc0cca66906 100644 (file)
@@ -391,7 +391,7 @@ When a widget receives the unmap signal, it must:
  2) If the widget does not have a window, unmap all child widgets
  3) Do any other functions related to taking the widget offscreen
      (for instance, removing popup windows...)
3) Unset GTK_MAPPED
4) Unset GTK_MAPPED
 
 
 The Unrealize signal
index b0fc10f8787c73e48cd417213679c7b2e4f485cb..313270024044de9b4cbcc5a841a836ee9879c307 100644 (file)
--- a/gdk/gdk.h
+++ b/gdk/gdk.h
@@ -517,6 +517,21 @@ gint        gdk_text_height    (GdkFont        *font,
 gint    gdk_char_height    (GdkFont        *font,
                             gchar           character);
 
+void     gdk_text_extents   (GdkFont     *font,
+                            const gchar *text,
+                            gint         text_length,
+                            gint        *lbearing,
+                            gint        *rbearing,
+                            gint        *width,
+                            gint        *ascent,
+                            gint        *descent);
+void     gdk_string_extents (GdkFont     *font,
+                            const gchar *string,
+                            gint        *lbearing,
+                            gint        *rbearing,
+                            gint        *width,
+                            gint        *ascent,
+                            gint        *descent);
 
 /* Drawing
  */
index acc82f2b625be2a26b58f31ec7cee69196cc1391..1b37930a73f0a88e516a2951cb23d2be19f1ca55 100644 (file)
@@ -328,6 +328,92 @@ gdk_string_measure (GdkFont     *font,
   return gdk_text_measure (font, string, strlen (string));
 }
 
+void
+gdk_text_extents (GdkFont     *font,
+                  const gchar *text,
+                  gint         text_length,
+                 gint        *lbearing,
+                 gint        *rbearing,
+                 gint        *width,
+                 gint        *ascent,
+                 gint        *descent)
+{
+  GdkFontPrivate *private;
+  XCharStruct overall;
+  XFontStruct *xfont;
+  XFontSet    fontset;
+  XRectangle  ink, logical;
+  int direction;
+  int font_ascent;
+  int font_descent;
+
+  g_return_if_fail (font != NULL);
+  g_return_if_fail (text != NULL);
+
+  private = (GdkFontPrivate*) font;
+
+  switch (font->type)
+    {
+    case GDK_FONT_FONT:
+      xfont = (XFontStruct *) private->xfont;
+      if ((xfont->min_byte1 == 0) && (xfont->max_byte1 == 0))
+       {
+         XTextExtents (xfont, text, text_length,
+                       &direction, &font_ascent, &font_descent,
+                       &overall);
+       }
+      else
+       {
+         XTextExtents16 (xfont, (XChar2b *) text, text_length / 2,
+                         &direction, &font_ascent, &font_descent,
+                         &overall);
+       }
+      if (lbearing)
+       *lbearing = overall.lbearing;
+      if (rbearing)
+       *rbearing = overall.rbearing;
+      if (width)
+       *width = overall.width;
+      if (ascent)
+       *ascent = overall.ascent;
+      if (descent)
+       *descent = overall.descent;
+      break;
+    case GDK_FONT_FONTSET:
+      fontset = (XFontSet) private->xfont;
+      XmbTextExtents (fontset, text, text_length, &ink, &logical);
+      if (lbearing)
+       *lbearing = -ink.x;
+      if (rbearing)
+       *rbearing = ink.y;
+      if (width)
+       *width = logical.width;
+      if (ascent)
+       *ascent = ink.height;
+      if (descent)
+       *descent = -ink.y;
+      break;
+    }
+
+}
+
+void
+gdk_string_extents (GdkFont     *font,
+                   const gchar *string,
+                   gint        *lbearing,
+                   gint        *rbearing,
+                   gint        *width,
+                   gint        *ascent,
+                   gint        *descent)
+{
+  g_return_if_fail (font != NULL);
+  g_return_if_fail (string != NULL);
+
+  gdk_text_extents (font, string, strlen (string),
+                   lbearing, rbearing, width, ascent, descent);
+}
+
+
 gint
 gdk_text_measure (GdkFont     *font,
                   const gchar *text,
index acc82f2b625be2a26b58f31ec7cee69196cc1391..1b37930a73f0a88e516a2951cb23d2be19f1ca55 100644 (file)
@@ -328,6 +328,92 @@ gdk_string_measure (GdkFont     *font,
   return gdk_text_measure (font, string, strlen (string));
 }
 
+void
+gdk_text_extents (GdkFont     *font,
+                  const gchar *text,
+                  gint         text_length,
+                 gint        *lbearing,
+                 gint        *rbearing,
+                 gint        *width,
+                 gint        *ascent,
+                 gint        *descent)
+{
+  GdkFontPrivate *private;
+  XCharStruct overall;
+  XFontStruct *xfont;
+  XFontSet    fontset;
+  XRectangle  ink, logical;
+  int direction;
+  int font_ascent;
+  int font_descent;
+
+  g_return_if_fail (font != NULL);
+  g_return_if_fail (text != NULL);
+
+  private = (GdkFontPrivate*) font;
+
+  switch (font->type)
+    {
+    case GDK_FONT_FONT:
+      xfont = (XFontStruct *) private->xfont;
+      if ((xfont->min_byte1 == 0) && (xfont->max_byte1 == 0))
+       {
+         XTextExtents (xfont, text, text_length,
+                       &direction, &font_ascent, &font_descent,
+                       &overall);
+       }
+      else
+       {
+         XTextExtents16 (xfont, (XChar2b *) text, text_length / 2,
+                         &direction, &font_ascent, &font_descent,
+                         &overall);
+       }
+      if (lbearing)
+       *lbearing = overall.lbearing;
+      if (rbearing)
+       *rbearing = overall.rbearing;
+      if (width)
+       *width = overall.width;
+      if (ascent)
+       *ascent = overall.ascent;
+      if (descent)
+       *descent = overall.descent;
+      break;
+    case GDK_FONT_FONTSET:
+      fontset = (XFontSet) private->xfont;
+      XmbTextExtents (fontset, text, text_length, &ink, &logical);
+      if (lbearing)
+       *lbearing = -ink.x;
+      if (rbearing)
+       *rbearing = ink.y;
+      if (width)
+       *width = logical.width;
+      if (ascent)
+       *ascent = ink.height;
+      if (descent)
+       *descent = -ink.y;
+      break;
+    }
+
+}
+
+void
+gdk_string_extents (GdkFont     *font,
+                   const gchar *string,
+                   gint        *lbearing,
+                   gint        *rbearing,
+                   gint        *width,
+                   gint        *ascent,
+                   gint        *descent)
+{
+  g_return_if_fail (font != NULL);
+  g_return_if_fail (string != NULL);
+
+  gdk_text_extents (font, string, strlen (string),
+                   lbearing, rbearing, width, ascent, descent);
+}
+
+
 gint
 gdk_text_measure (GdkFont     *font,
                   const gchar *text,
index 0828c1acbf13716f0bc4aedfd2cddcd98790fa1d..f61ae5d56ecadd5aca73eccf08202b5c9c48c15a 100644 (file)
@@ -84,6 +84,7 @@ libgtk_1_1_la_SOURCES = \
        gtkstyle.c              \
        gtkstatusbar.c          \
        gtktable.c              \
+       gtktearoffmenuitem.c    \
        gtktext.c               \
        gtktipsquery.c          \
        gtktogglebutton.c       \
@@ -190,6 +191,7 @@ gtkinclude_HEADERS = \
        gtkstyle.h              \
        gtkstatusbar.h          \
        gtktable.h              \
+       gtktearoffmenuitem.h    \
        gtktext.h               \
        gtktipsquery.h          \
        gtktogglebutton.h       \
index 181a95047d2b7eb029eaf60cdad8b82838c8e259..024fcf5d0f1a6680ea78b92a805bd79fabe88af4 100644 (file)
    (exact GTK_MATCH_EXACT)
    (last GTK_MATCH_LAST))
 
+(define-enum GtkMenuDirectionType
+   (parent GTK_MENU_DIR_PARENT)
+   (child GTK_MENU_DIR_CHILD)
+   (next GTK_MENU_DIR_NEXT)
+   (prev GTK_MENU_DIR_PREV))
+
 (define-enum GtkMenuFactoryType
    (menu GTK_MENU_FACTORY_MENU)
    (menu-bar GTK_MENU_FACTORY_MENU_BAR)
index 7eb797f3f88275089c0e7db8054dde83feb5e385..d5ce6a4a966aa67fa601909c8ed95cd642be9cdf 100644 (file)
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
 #include <gtk/gtkstyle.h>
 #include <gtk/gtkstatusbar.h>
 #include <gtk/gtktable.h>
+#include <gtk/gtktearoffmenuitem.h>
 #include <gtk/gtktext.h>
 #include <gtk/gtktipsquery.h>
 #include <gtk/gtktogglebutton.h>
index 62ff1644d8f76daf4f5d568c126d42469bbc6dac..c21050c269b90dcce5a712585636b66b2b5d9d2b 100644 (file)
@@ -151,6 +151,8 @@ gtk_check_menu_item_class_init (GtkCheckMenuItemClass *klass)
   menu_item_class->activate = gtk_check_menu_item_activate;
   menu_item_class->toggle_size = 12;
 
+  menu_item_class->hide_on_activate = FALSE;
+
   klass->toggled = NULL;
   klass->draw_indicator = gtk_real_check_menu_item_draw_indicator;
 }
index 86504ea756dbc3124b828e40192deee116efa467..bddc81bbe11fd315501aa84a4e5eda701b9e11a2 100644 (file)
@@ -107,6 +107,15 @@ typedef enum
   GTK_MATCH_LAST
 } GtkMatchType;
 
+/* Menu keyboard movement types */
+typedef enum
+{
+  GTK_MENU_DIR_PARENT,
+  GTK_MENU_DIR_CHILD,
+  GTK_MENU_DIR_NEXT,
+  GTK_MENU_DIR_PREV
+} GtkMenuDirectionType;
+
 typedef enum
 {
   GTK_MENU_FACTORY_MENU,
index 869bc574f894045a3cf1a1d74eb86ba3515a8e9f..a4a6f6a7bdbfa831713e549188c4c90b1a2d32be 100644 (file)
@@ -190,7 +190,8 @@ gtk_item_realize (GtkWidget *widget)
                           GDK_BUTTON_PRESS_MASK |
                           GDK_BUTTON_RELEASE_MASK |
                           GDK_ENTER_NOTIFY_MASK |
-                          GDK_LEAVE_NOTIFY_MASK);
+                          GDK_LEAVE_NOTIFY_MASK |
+                          GDK_POINTER_MOTION_MASK);
 
   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
index 0a624f060562588bff1aa4c804e1da9b154ef19c..dd605d23442da78be8dd6a5fb91ffca374bf2763 100644 (file)
 #include       "gtk/gtkmenuitem.h"
 #include       "gtk/gtkradiomenuitem.h"
 #include       "gtk/gtkcheckmenuitem.h"
+#include       "gtk/gtktearoffmenuitem.h"
 #include       "gtk/gtkaccellabel.h"
 #include       "gdk/gdkprivate.h" /* for gdk_progname */
+#include        "gdk/gdkkeysyms.h"
 #include       <string.h>
 #include       <sys/stat.h>
 #include       <fcntl.h>
@@ -98,6 +100,8 @@ static const gchar   *key_type_check_item = "<CheckItem>";
 static GQuark           quark_type_check_item = 0;
 static const gchar     *key_type_toggle_item = "<ToggleItem>";
 static GQuark           quark_type_toggle_item = 0;
+static const gchar     *key_type_tearoff_item = "<Tearoff>";
+static GQuark           quark_type_tearoff_item = 0;
 static const gchar     *key_type_separator_item = "<Separator>";
 static GQuark           quark_type_separator_item = 0;
 static const gchar     *key_type_branch = "<Branch>";
@@ -210,6 +214,7 @@ gtk_item_factory_class_init (GtkItemFactoryClass  *class)
   quark_type_radio_item = g_quark_from_static_string (key_type_radio_item);
   quark_type_check_item = g_quark_from_static_string (key_type_check_item);
   quark_type_toggle_item = g_quark_from_static_string (key_type_toggle_item);
+  quark_type_tearoff_item = g_quark_from_static_string (key_type_tearoff_item);
   quark_type_separator_item = g_quark_from_static_string (key_type_separator_item);
   quark_type_branch = g_quark_from_static_string (key_type_branch);
   quark_type_last_branch = g_quark_from_static_string (key_type_last_branch);
@@ -547,6 +552,7 @@ gtk_item_factory_construct (GtkItemFactory  *ifactory,
                            const gchar         *path,
                            GtkAccelGroup       *accel_group)
 {
+  GtkAccelGroup *menu_group;
   guint len;
 
   g_return_if_fail (ifactory != NULL);
@@ -579,6 +585,10 @@ gtk_item_factory_construct (GtkItemFactory *ifactory,
                    NULL);
   gtk_object_ref (GTK_OBJECT (ifactory));
   gtk_object_sink (GTK_OBJECT (ifactory));
+
+  menu_group = gtk_accel_group_new ();
+  gtk_accel_group_attach (menu_group, GTK_OBJECT (ifactory->widget));
+    
   /*
     gtk_signal_connect_object_while_alive (GTK_OBJECT (ifactory->widget),
                                         "destroy",
@@ -880,6 +890,44 @@ gtk_item_factory_get_widget_by_action (GtkItemFactory   *ifactory,
   return NULL;
 }
 
+static gboolean
+gtk_item_factory_parse_path (gchar  *str,
+                            gchar **path,
+                            gchar **parent_path,
+                            gchar **item)
+{
+  gchar *p, *q;
+
+  *path = g_strdup (str);
+
+  p = q = *path;
+  while (*p)
+    {
+      if (*p != '_')
+       {
+         *q++ = *p;
+       }
+      p++;
+    }
+  *q = 0;
+
+  *parent_path = g_strdup (*path);
+  p = strrchr (*parent_path, '/');
+  if (!p)
+    {
+      g_warning ("GtkItemFactory: invalid entry path `%s'", str);
+      return FALSE;
+    }
+  *p = 0;
+
+  p = strrchr (str, '/');
+  p++;
+
+  *item = g_strdup (p);
+
+  return TRUE;
+}
+
 void
 gtk_item_factory_create_item (GtkItemFactory        *ifactory,
                              GtkItemFactoryEntry    *entry,
@@ -889,11 +937,15 @@ gtk_item_factory_create_item (GtkItemFactory           *ifactory,
   GtkWidget *parent;
   GtkWidget *widget;
   GSList *radio_group;
+  gchar *name;
   gchar *parent_path;
-  gchar *p;
+  gchar *path;
+  guint accel_key;
   guint type_id;
   GtkType type;
   gchar *item_type_path;
+  GtkAccelGroup *parent_accel_group = NULL;
+  GSList *tmp_list;
 
   g_return_if_fail (ifactory != NULL);
   g_return_if_fail (GTK_IS_ITEM_FACTORY (ifactory));
@@ -923,6 +975,8 @@ gtk_item_factory_create_item (GtkItemFactory             *ifactory,
     type = GTK_TYPE_RADIO_MENU_ITEM;
   else if (type_id == quark_type_check_item)
     type = GTK_TYPE_CHECK_MENU_ITEM;
+  else if (type_id == quark_type_tearoff_item)
+    type = GTK_TYPE_TEAROFF_MENU_ITEM;
   else if (type_id == quark_type_toggle_item)
     type = GTK_TYPE_CHECK_MENU_ITEM;
   else if (type_id == quark_type_separator_item)
@@ -949,16 +1003,11 @@ gtk_item_factory_create_item (GtkItemFactory          *ifactory,
          return;
        }
     }
-  
-  parent_path = g_strdup (entry->path);
-  p = strrchr (parent_path, '/');
-  if (!p)
-    {
-      g_warning ("GtkItemFactory: invalid entry path `%s'", entry->path);
-      return;
-    }
-  *p = 0;
 
+  if (!gtk_item_factory_parse_path (entry->path, 
+                                   &path, &parent_path, &name))
+    return;
+  
   parent = gtk_item_factory_get_widget (ifactory, parent_path);
   if (!parent)
     {
@@ -977,9 +1026,10 @@ gtk_item_factory_create_item (GtkItemFactory           *ifactory,
   g_free (parent_path);
 
   g_return_if_fail (parent != NULL);
-  
-  p = strrchr (entry->path, '/');
-  p++;
+
+  tmp_list = gtk_accel_groups_from_object (GTK_OBJECT (parent));
+  if (tmp_list)
+    parent_accel_group = tmp_list->data;
   
   widget = gtk_widget_new (type,
                           "GtkWidget::visible", TRUE,
@@ -993,22 +1043,50 @@ gtk_item_factory_create_item (GtkItemFactory          *ifactory,
   if (GTK_IS_CHECK_MENU_ITEM (widget))
     gtk_check_menu_item_set_show_toggle (GTK_CHECK_MENU_ITEM (widget), TRUE);
     
-  if (type_id != quark_type_separator_item && *p)
+  if ((type_id != quark_type_separator_item) && 
+      (type_id != quark_type_tearoff_item) &&
+      *name)
     {
       GtkWidget *label;
-      
+
       label =
        gtk_widget_new (GTK_TYPE_ACCEL_LABEL,
-                       "GtkLabel::label", p,
                        "GtkWidget::visible", TRUE,
                        "GtkWidget::parent", widget,
                        "GtkAccelLabel::accel_widget", widget,
                        "GtkMisc::xalign", 0.0,
                        NULL);
+
+      accel_key = gtk_label_parse_uline (GTK_LABEL (label), name);
+
+      if ((accel_key != GDK_VoidSymbol) && GTK_IS_MENU_BAR (parent))
+       {
+         gtk_widget_add_accelerator (widget,
+                                     "activate_item",
+                                     ifactory->accel_group,
+                                     accel_key, GDK_MOD1_MASK,
+                                     GTK_ACCEL_LOCKED);
+       }
+
+      if ((accel_key != GDK_VoidSymbol) && parent_accel_group)
+       {
+         gtk_widget_add_accelerator (widget,
+                                     "activate_item",
+                                     parent_accel_group,
+                                     accel_key, 0,
+                                     GTK_ACCEL_LOCKED);
+       }
     }
+
+  g_free (name);
+
   if (type_id == quark_type_branch ||
       type_id == quark_type_last_branch)
     {
+      GtkAccelGroup *menu_group;
+
+      menu_group = gtk_accel_group_new ();
+      
       if (type_id == quark_type_last_branch)
        gtk_menu_item_right_justify (GTK_MENU_ITEM (widget));
        
@@ -1016,11 +1094,13 @@ gtk_item_factory_create_item (GtkItemFactory         *ifactory,
       widget =
        gtk_widget_new (GTK_TYPE_MENU,
                        NULL);
+      
+      gtk_accel_group_attach (menu_group, GTK_OBJECT (widget));
       gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), widget);
     }     
   
   gtk_item_factory_add_item (ifactory,
-                            entry->path, entry->accelerator,
+                            path, entry->accelerator,
                             entry->callback, entry->callback_action, callback_data,
                             callback_type,
                             item_type_path,
index e96db1b3a6fd093e794f1ae41ef459a11ca79bc9..9606ef549d709f1c7d41c6cc06aa284c5e08ea79 100644 (file)
  */
 #include <string.h>
 #include "gtklabel.h"
-
+#include "gdk/gdkkeysyms.h"
 
 enum {
   ARG_0,
   ARG_LABEL,
+  ARG_PATTERN,
   ARG_JUSTIFY
 };
 
+typedef struct _GtkLabelRow GtkLabelRow;
+
+struct _GtkLabelRow {
+  gint index;
+  gint width;
+  gint height;
+  gint len;
+};
+
+GMemChunk *row_mem_chunk = NULL;
+
 static void gtk_label_class_init   (GtkLabelClass  *klass);
 static void gtk_label_init        (GtkLabel       *label);
 static void gtk_label_set_arg     (GtkObject      *object,
@@ -43,6 +55,7 @@ static void gtk_label_state_changed (GtkWidget            *widget,
                                     guint           previous_state);
 static void gtk_label_style_set            (GtkWidget      *widget,
                                     GtkStyle       *previous_style);
+static void gtk_label_free_rows     (GtkLabel       *label);
 
 
 
@@ -87,6 +100,7 @@ gtk_label_class_init (GtkLabelClass *class)
   parent_class = gtk_type_class (gtk_misc_get_type ());
   
   gtk_object_add_arg_type ("GtkLabel::label", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL);
+  gtk_object_add_arg_type ("GtkLabel::pattern", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_PATTERN);
   gtk_object_add_arg_type ("GtkLabel::justify", GTK_TYPE_JUSTIFICATION, GTK_ARG_READWRITE, ARG_JUSTIFY);
 
   object_class->set_arg = gtk_label_set_arg;
@@ -113,6 +127,9 @@ gtk_label_set_arg (GtkObject          *object,
     case ARG_LABEL:
       gtk_label_set (label, GTK_VALUE_STRING (*arg) ? GTK_VALUE_STRING (*arg) : "");
       break;
+    case ARG_PATTERN:
+      gtk_label_set_pattern (label, GTK_VALUE_STRING (*arg));
+      break;
     case ARG_JUSTIFY:
       gtk_label_set_justify (label, GTK_VALUE_ENUM (*arg));
       break;
@@ -135,6 +152,9 @@ gtk_label_get_arg (GtkObject          *object,
     case ARG_LABEL:
       GTK_VALUE_STRING (*arg) = g_strdup (label->label);
       break;
+    case ARG_PATTERN:
+      GTK_VALUE_STRING (*arg) = g_strdup (label->pattern);
+      break;
     case ARG_JUSTIFY:
       GTK_VALUE_ENUM (*arg) = label->jtype;
       break;
@@ -154,6 +174,7 @@ gtk_label_init (GtkLabel *label)
   label->max_width = 0;
   label->jtype = GTK_JUSTIFY_CENTER;
   label->needs_clear = FALSE;
+  label->pattern = NULL;
   
   gtk_label_set (label, "");
 }
@@ -181,18 +202,57 @@ gtk_label_set (GtkLabel      *label,
   g_return_if_fail (label != NULL);
   g_return_if_fail (GTK_IS_LABEL (label));
   g_return_if_fail (str != NULL);
+
+  if (!row_mem_chunk)
+    row_mem_chunk = g_mem_chunk_create (GtkLabelRow, 64, G_ALLOC_AND_FREE);
   
   if (label->label)
     g_free (label->label);
   label->label = g_strdup (str);
   
   if (label->row)
-    g_slist_free (label->row);
-  label->row = NULL;
-  label->row = g_slist_append (label->row, label->label);
+    gtk_label_free_rows (label);
+
   p = label->label;
-  while ((p = strchr(p, '\n')))
-    label->row = g_slist_append (label->row, ++p);
+  do {
+    GtkLabelRow *row = g_chunk_new (GtkLabelRow, row_mem_chunk);
+    label->row = g_slist_append (label->row, row);
+
+    row->index = p - label->label;
+    
+    p = strchr(p, '\n');
+    if (p)
+      {
+       p++;
+       row->len = (p - label->label) - row->index;
+      }
+    else
+      row->len = strlen (label->label) - row->index;
+  } while (p);
+
+  if (GTK_WIDGET_VISIBLE (label))
+    {
+      if (GTK_WIDGET_MAPPED (label))
+       gdk_window_clear_area (GTK_WIDGET (label)->window,
+                              GTK_WIDGET (label)->allocation.x,
+                              GTK_WIDGET (label)->allocation.y,
+                              GTK_WIDGET (label)->allocation.width,
+                              GTK_WIDGET (label)->allocation.height);
+      
+      gtk_widget_queue_resize (GTK_WIDGET (label));
+    }
+}
+
+void
+gtk_label_set_pattern (GtkLabel           *label,
+                      const gchar *pattern)
+{
+  g_return_if_fail (label != NULL);
+  g_return_if_fail (GTK_IS_LABEL (label));
+  
+  if (label->pattern)
+    g_free (label->pattern);
+  label->pattern = g_strdup (pattern);
   
   if (GTK_WIDGET_VISIBLE (label))
     {
@@ -255,17 +315,128 @@ gtk_label_finalize (GtkObject *object)
   label = GTK_LABEL (object);
   
   g_free (label->label);
-  g_slist_free (label->row);
+  gtk_label_free_rows (label);
   
   (* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
 }
 
+static gint
+gtk_label_process_row (GtkLabel    *label,
+                      GtkLabelRow *row,
+                      gint         x, gint y,
+                      gboolean     draw)
+{
+  GtkWidget *widget = GTK_WIDGET (label);
+  
+  char lastc;
+  gint i, j;
+  gint offset;
+  gint height;
+  gint pattern_length;
+
+  if (label->pattern) 
+    pattern_length = strlen (label->pattern);
+  else
+    pattern_length = 0;
+
+  offset = 0;
+  height = widget->style->font->ascent + widget->style->font->descent;
+  
+  if (draw)
+    {
+      if (label->jtype == GTK_JUSTIFY_CENTER)
+       offset = (label->max_width - row->width) / 2;
+      else if (label->jtype == GTK_JUSTIFY_RIGHT)
+       offset = (label->max_width - row->width);
+    }
+      
+  if (label->pattern && (row->index < pattern_length))
+    lastc = label->pattern[0];
+  else
+    lastc = ' ';
+  
+  j = 0;
+  for (i=1; i<=row->len; i++)
+    {
+      char c;
+
+      if (label->pattern && (row->index + i < pattern_length))
+       c = label->pattern[row->index+i];
+      else
+       c = ' ';
+
+      if ((i == row->len) || (lastc != c))
+       {
+         gint width = 0;
+
+         if (lastc == '_')
+           {
+             gint descent;
+             gint rbearing;
+             gint lbearing;
+
+             gdk_text_extents (widget->style->font,
+                               &label->label[row->index+j], i - j,
+                               &lbearing, &rbearing, &width, NULL, &descent);
+
+             if (draw)
+               {
+                 if (widget->state == GTK_STATE_INSENSITIVE)
+                   gdk_draw_line (widget->window,
+                                  widget->style->white_gc,
+                                  offset + x + lbearing, y + descent + 2, 
+                                  offset + x + rbearing + 1, y + descent + 2);
+             
+                 gdk_draw_line (widget->window,
+                                widget->style->fg_gc[widget->state],
+                                offset + x + lbearing - 1, y + descent + 1, 
+                                offset + x + rbearing, y + descent + 1);
+               }
+
+             height = MAX (height, 
+                           widget->style->font->ascent + descent + 2);
+           }
+         else if (i != row->len)
+           {
+             width = gdk_text_width (widget->style->font, 
+                                     &label->label[row->index+j], 
+                                     i - j);
+           }
+
+         if (draw)
+           {
+             if (widget->state == GTK_STATE_INSENSITIVE)
+               gdk_draw_text (widget->window, widget->style->font,
+                              widget->style->white_gc,
+                              offset + x + 1, y + 1,
+                              &label->label[row->index+j], i - j);
+             
+             gdk_draw_text (widget->window, widget->style->font,
+                            widget->style->fg_gc[widget->state],
+                            offset + x, y, 
+                            &label->label[row->index+j], i - j);
+           }
+
+                 
+         offset += width;
+
+         if (i != row->len)
+           {
+             lastc = c;
+             j = i;
+           }
+       }
+    }
+
+  return height;
+}
+
 static void
 gtk_label_size_request (GtkWidget      *widget,
                        GtkRequisition *requisition)
 {
   GtkLabel *label;
-  GSList *row;
+  GSList *tmp_list;
   gint width;
   
   g_return_if_fail (widget != NULL);
@@ -274,38 +445,35 @@ gtk_label_size_request (GtkWidget      *widget,
   
   label = GTK_LABEL (widget);
   
-  row = label->row;
+  requisition->height = label->misc.ypad * 2;
+
+  tmp_list = label->row;
   width = 0;
-  while (row)
+  while (tmp_list)
     {
-      if (row->next)
-       width = MAX (width,
-                    gdk_text_width (GTK_WIDGET (label)->style->font,
-                                    row->data,
-                                    (gchar*) row->next->data - (gchar*) row->data - 1));
-      else
-       width = MAX (width, gdk_string_width (GTK_WIDGET (label)->style->font, row->data));
-      row = row->next;
+      GtkLabelRow *row = tmp_list->data;
+
+      row->width = gdk_text_width (GTK_WIDGET (label)->style->font,
+                                  &label->label [row->index],
+                                  row->len);
+      width = MAX (width, row->width);
+
+      requisition->height += gtk_label_process_row (label, row, 0, 0, FALSE) + 2;
+
+      tmp_list = tmp_list->next;
     }
 
   label->max_width = width;
   requisition->width = width + label->misc.xpad * 2;
-  requisition->height = ((GTK_WIDGET (label)->style->font->ascent +
-                         GTK_WIDGET (label)->style->font->descent + 2) *
-                        g_slist_length(label->row) +
-                        label->misc.ypad * 2);
 }
-
+  
 static gint
 gtk_label_expose (GtkWidget     *widget,
                  GdkEventExpose *event)
 {
   GtkLabel *label;
   GtkMisc *misc;
-  GSList *row;
-  gint state;
-  gint offset;
-  gint len;
+  GSList *tmp_list;
   gint x, y;
   
   g_return_val_if_fail (widget != NULL, FALSE);
@@ -317,13 +485,11 @@ gtk_label_expose (GtkWidget        *widget,
       label = GTK_LABEL (widget);
       misc = GTK_MISC (widget);
       
-      state = widget->state;
-      
       /*
        * GC Clipping
        */
       gdk_gc_set_clip_rectangle (widget->style->white_gc, &event->area);
-      gdk_gc_set_clip_rectangle (widget->style->fg_gc[state], &event->area);
+      gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state], &event->area);
       
       /* We clear the whole allocation here so that if a partial
        * expose is triggered we don't just clear part and mess up
@@ -351,59 +517,41 @@ gtk_label_expose (GtkWidget        *widget,
            (widget->requisition.height - misc->ypad * 2)) *
           misc->yalign + widget->style->font->ascent) + 1.5;
       
-      row = label->row;
-      while (row && row->next)
-       {
-         len = (gchar*) row->next->data - (gchar*) row->data - 1;
-         offset = 0;
-         
-         if (label->jtype == GTK_JUSTIFY_CENTER)
-           offset = (label->max_width - gdk_text_width (widget->style->font, row->data, len)) / 2;
-         
-         else if (label->jtype == GTK_JUSTIFY_RIGHT)
-           offset = (label->max_width - gdk_text_width (widget->style->font, row->data, len));
-         
-         if (state == GTK_STATE_INSENSITIVE)
-           gdk_draw_text (widget->window, widget->style->font,
-                          widget->style->white_gc,
-                          offset + x + 1, y + 1, row->data, len);
-         
-         gdk_draw_text (widget->window, widget->style->font,
-                        widget->style->fg_gc[state],
-                        offset + x, y, row->data, len);
-         row = row->next;
-         y += widget->style->font->ascent + widget->style->font->descent + 2;
-       }
-      
       /* 
        * COMMENT: we can avoid gdk_text_width() calls here storing in label->row
        * the widths of the rows calculated in gtk_label_set.
        * Once we have a wrapping interface we can support GTK_JUSTIFY_FILL.
        */
-      offset = 0;
-      
-      if (label->jtype == GTK_JUSTIFY_CENTER)
-       offset = (label->max_width - gdk_string_width (widget->style->font, row->data)) / 2;
-      
-      else if (label->jtype == GTK_JUSTIFY_RIGHT)
-       offset = (label->max_width - gdk_string_width (widget->style->font, row->data));
-      
-      if (state == GTK_STATE_INSENSITIVE)
-       gdk_draw_string (widget->window, widget->style->font,
-                        widget->style->white_gc,
-                        offset + x + 1, y + 1, row->data);
-      
-      gdk_draw_string (widget->window, widget->style->font,
-                      widget->style->fg_gc[state],
-                      offset + x, y, row->data);
+
+      tmp_list = label->row;
+      while (tmp_list)
+       {
+         y += gtk_label_process_row (label, tmp_list->data, x, y, TRUE) + 2;
+         tmp_list = tmp_list->next;
+       }
       
       gdk_gc_set_clip_mask (widget->style->white_gc, NULL);
-      gdk_gc_set_clip_mask (widget->style->fg_gc[state], NULL);
+      gdk_gc_set_clip_mask (widget->style->fg_gc[widget->state], NULL);
       
     }
   return TRUE;
 }
 
+static void
+gtk_label_free_rows (GtkLabel *label)
+{
+  GSList *tmp_list;
+
+  tmp_list = label->row;
+  while (tmp_list)
+    {
+      g_mem_chunk_free (row_mem_chunk, tmp_list->data);
+      tmp_list = tmp_list->next;
+    }
+  g_slist_free (label->row);
+  label->row = NULL;
+}
+
 static void 
 gtk_label_state_changed (GtkWidget     *widget,
                         guint           previous_state)
@@ -420,3 +568,67 @@ gtk_label_style_set (GtkWidget     *widget,
     GTK_LABEL (widget)->needs_clear = TRUE;
 }
 
+guint      
+gtk_label_parse_uline (GtkLabel         *label,
+                      const gchar      *string)
+{
+  guint accel_key = GDK_VoidSymbol;
+  const gchar *p;
+  gchar *q, *r;
+  gchar *name, *pattern;
+
+  gint length;
+  gboolean underscore;
+
+  length = strlen (string);
+  
+  name = g_new (gchar, length+1);
+  pattern = g_new (gchar, length+1);
+
+  underscore = FALSE;
+
+  p = string;
+  q = name;
+  r = pattern;
+  underscore = FALSE;
+
+  while (*p)
+    {
+      if (underscore)
+       {
+         if (*p == '_')
+           *r++ = ' ';
+         else
+           {
+             *r++ = '_';
+             if (accel_key == GDK_VoidSymbol)
+               accel_key = gdk_keyval_to_lower (*p);
+           }
+
+         *q++ = *p;
+         underscore = FALSE;
+       }
+      else
+       {
+         if (*p == '_')
+           underscore = TRUE;
+         else
+           {
+             *q++ = *p;
+             *r++ = ' ';
+           }
+       }
+      p++;
+    }
+  *q = 0;
+
+  gtk_label_set (label, name);
+  gtk_label_set_pattern (label, pattern);
+  
+  g_free (name);
+  g_free (pattern);
+
+  return accel_key;
+}
+
+
index 331b2a788f64f2547241accaa7b8691389313ba7..17275dae9de988a3a02f28f4ec279d9da18ea795 100644 (file)
@@ -44,6 +44,8 @@ struct _GtkLabel
   GtkMisc misc;
   
   gchar  *label;
+  gchar  *pattern;
+
   GSList *row;
   guint          max_width : 16;
   guint   jtype : 2;
@@ -60,12 +62,22 @@ GtkType        gtk_label_get_type    (void);
 GtkWidget* gtk_label_new        (const gchar       *string);
 void      gtk_label_set         (GtkLabel          *label,
                                  const gchar       *string);
+void      gtk_label_set_pattern (GtkLabel          *label,
+                                 const gchar       *pattern);
 void      gtk_label_set_justify (GtkLabel          *label,
                                  GtkJustification   jtype);
 void      gtk_label_get         (GtkLabel          *label,
                                  gchar            **string);
 
 
+/* Convenience function to set the name and pattern by parsing
+ * a string with embedded underscores, and return the appropriate
+ * key symbol for the accelerator.
+ */
+
+guint      gtk_label_parse_uline    (GtkLabel         *label,
+                                    const gchar      *string);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
index d3cb999e12ecdf310203847af05a43941945e1cb..9025e071c3bd732f437f82916c3bafb12d674f74 100644 (file)
  */
 #include <ctype.h>
 #include "gdk/gdkkeysyms.h"
+#include "gtkbindings.h"
+#include "gtklabel.h"
 #include "gtkmain.h"
 #include "gtkmenu.h"
 #include "gtkmenuitem.h"
 #include "gtksignal.h"
+#include "gtkwindow.h"
 
 
 #define MENU_ITEM_CLASS(w)   GTK_MENU_ITEM_CLASS (GTK_OBJECT (w)->klass)
@@ -39,9 +42,6 @@ struct _GtkMenuAttachData
 static void gtk_menu_class_init            (GtkMenuClass      *klass);
 static void gtk_menu_init          (GtkMenu           *menu);
 static void gtk_menu_destroy       (GtkObject         *object);
-static void gtk_menu_show          (GtkWidget         *widget);
-static void gtk_menu_map           (GtkWidget         *widget);
-static void gtk_menu_unmap         (GtkWidget         *widget);
 static void gtk_menu_realize       (GtkWidget         *widget);
 static void gtk_menu_size_request   (GtkWidget        *widget,
                                     GtkRequisition    *requisition);
@@ -52,14 +52,14 @@ static void gtk_menu_draw       (GtkWidget         *widget,
                                     GdkRectangle      *area);
 static gint gtk_menu_expose        (GtkWidget         *widget,
                                     GdkEventExpose    *event);
-static gint gtk_menu_configure     (GtkWidget         *widget,
-                                    GdkEventConfigure *event);
 static gint gtk_menu_key_press     (GtkWidget         *widget,
                                     GdkEventKey       *event);
-static void gtk_menu_check_resize    (GtkContainer      *container);
+static gint gtk_menu_motion_notify  (GtkWidget        *widget,
+                                    GdkEventMotion    *event);
 static void gtk_menu_deactivate            (GtkMenuShell      *menu_shell);
 static void gtk_menu_show_all      (GtkWidget         *widget);
 static void gtk_menu_hide_all      (GtkWidget         *widget);
+static void gtk_menu_position       (GtkMenu           *menu);
 
 static GtkMenuShellClass *parent_class = NULL;
 static const gchar     *attach_data_key = "gtk-menu-attach-data";
@@ -97,6 +97,8 @@ gtk_menu_class_init (GtkMenuClass *class)
   GtkWidgetClass *widget_class;
   GtkContainerClass *container_class;
   GtkMenuShellClass *menu_shell_class;
+
+  GtkBindingSet *binding_set;
   
   object_class = (GtkObjectClass*) class;
   widget_class = (GtkWidgetClass*) class;
@@ -106,57 +108,89 @@ gtk_menu_class_init (GtkMenuClass *class)
   
   object_class->destroy = gtk_menu_destroy;
   
-  widget_class->show = gtk_menu_show;
-  widget_class->map = gtk_menu_map;
-  widget_class->unmap = gtk_menu_unmap;
   widget_class->realize = gtk_menu_realize;
   widget_class->draw = gtk_menu_draw;
   widget_class->size_request = gtk_menu_size_request;
   widget_class->size_allocate = gtk_menu_size_allocate;
   widget_class->expose_event = gtk_menu_expose;
-  widget_class->configure_event = gtk_menu_configure;
   widget_class->key_press_event = gtk_menu_key_press;
+  widget_class->motion_notify_event = gtk_menu_motion_notify;
   widget_class->show_all = gtk_menu_show_all;
   widget_class->hide_all = gtk_menu_hide_all;  
   
-  container_class->check_resize = gtk_menu_check_resize;
-  
   menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
   menu_shell_class->deactivate = gtk_menu_deactivate;
+
+  binding_set = gtk_binding_set_by_class (class);
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_Up, 0,
+                               "move_current", 1,
+                               GTK_TYPE_MENU_DIRECTION_TYPE,
+                               GTK_MENU_DIR_PREV);
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_Down, 0,
+                               "move_current", 1,
+                               GTK_TYPE_MENU_DIRECTION_TYPE,
+                               GTK_MENU_DIR_NEXT);
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_Left, 0,
+                               "move_current", 1,
+                               GTK_TYPE_MENU_DIRECTION_TYPE,
+                               GTK_MENU_DIR_PARENT);
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_Right, 0,
+                               "move_current", 1,
+                               GTK_TYPE_MENU_DIRECTION_TYPE,
+                               GTK_MENU_DIR_CHILD);
 }
 
 static void
 gtk_menu_init (GtkMenu *menu)
 {
-  GTK_WIDGET_SET_FLAGS (menu, GTK_TOPLEVEL);
-  
-  gtk_container_set_resize_mode (GTK_CONTAINER (menu), GTK_RESIZE_QUEUE);
-  
   menu->parent_menu_item = NULL;
   menu->old_active_menu_item = NULL;
   menu->accel_group = NULL;
   menu->position_func = NULL;
   menu->position_func_data = NULL;
 
+  menu->toplevel = gtk_window_new (GTK_WINDOW_POPUP);
+  gtk_signal_connect_object (GTK_OBJECT (menu->toplevel),  "key_press_event",
+                            GTK_SIGNAL_FUNC (gtk_menu_key_press), 
+                            GTK_OBJECT (menu));
+  gtk_window_set_policy (GTK_WINDOW (menu->toplevel),
+                        FALSE, FALSE, TRUE);
+
+  gtk_container_add (GTK_CONTAINER (menu->toplevel), GTK_WIDGET (menu));
+
+  menu->tearoff_window = NULL;
+  menu->torn_off = FALSE;
+
   MENU_NEEDS_RESIZE (menu) = TRUE;
 }
 
 static void
 gtk_menu_destroy (GtkObject        *object)
 {
+  GtkMenu *menu;
   GtkMenuAttachData *data;
   
   g_return_if_fail (object != NULL);
   g_return_if_fail (GTK_IS_MENU (object));
+
+  menu = GTK_MENU (object);
   
   gtk_widget_ref (GTK_WIDGET (object));
   
   data = gtk_object_get_data (object, attach_data_key);
   if (data)
-    gtk_menu_detach (GTK_MENU (object));
+    gtk_menu_detach (menu);
   
-  gtk_menu_set_accel_group (GTK_MENU (object), NULL);
+  gtk_menu_set_accel_group (menu, NULL);
   
+  gtk_widget_destroy (menu->toplevel);
+  if (menu->tearoff_window)
+    gtk_widget_destroy (menu->tearoff_window);
+
   if (GTK_OBJECT_CLASS (parent_class)->destroy)
     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
   
@@ -283,22 +317,75 @@ gtk_menu_popup (GtkMenu               *menu,
                guint                button,
                guint32              activate_time)
 {
+  GtkWidget *widget;
   GtkWidget *xgrab_shell;
   GtkWidget *parent;
+  GdkEvent *current_event;
+  GtkMenuShell *menu_shell;
   
   g_return_if_fail (menu != NULL);
   g_return_if_fail (GTK_IS_MENU (menu));
   
-  GTK_MENU_SHELL (menu)->parent_menu_shell = parent_menu_shell;
-  GTK_MENU_SHELL (menu)->active = TRUE;
-  GTK_MENU_SHELL (menu)->button = button;
+  widget = GTK_WIDGET (menu);
+  menu_shell = GTK_MENU_SHELL (menu);
+  
+  menu_shell->parent_menu_shell = parent_menu_shell;
+  menu_shell->active = TRUE;
+  menu_shell->button = button;
+
+  /* If we are popping up the menu from something other than, a button
+   * press then, as a heuristic, we ignore enter events for the menu
+   * until we get a MOTION_NOTIFY.  
+   */
+
+  current_event = gtk_get_current_event();
+  if (current_event)
+    {
+      if ((current_event->type != GDK_BUTTON_PRESS) &&
+         (current_event->type != GDK_ENTER_NOTIFY))
+       menu_shell->ignore_enter = TRUE;
+    }
+
+  if (menu->torn_off)
+    {
+      GdkPixmap *pixmap;
+      GdkGC *gc;
+      GdkGCValues gc_values;
+      
+      gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS;
+      gc = gdk_gc_new_with_values (widget->window,
+                                  &gc_values, GDK_GC_SUBWINDOW);
+      
+      pixmap = gdk_pixmap_new (widget->window,
+                              widget->requisition.width,
+                              widget->requisition.height,
+                              -1);
+      
+      gdk_draw_pixmap (pixmap, gc,
+                      widget->window,
+                      0, 0, 0, 0, -1, -1);
+      gdk_gc_unref(gc);
+      
+      gdk_window_set_back_pixmap (menu->tearoff_window->window, pixmap, FALSE);
+      gdk_pixmap_unref (pixmap);
+
+      gtk_container_remove (GTK_CONTAINER (menu->tearoff_window), widget);
+      gtk_container_add (GTK_CONTAINER (menu->toplevel), widget);
+    }
   
   menu->parent_menu_item = parent_menu_item;
   menu->position_func = func;
   menu->position_func_data = data;
-  GTK_MENU_SHELL (menu)->activate_time = activate_time;
-  
+  menu_shell->activate_time = activate_time;
+
+  gtk_menu_position (menu);
+
+  /* We need to show the menu _here_ because code expects to be
+   * able to tell if the menu is onscreen by looking at the
+   * GTK_WIDGET_VISIBLE (menu)
+   */
   gtk_widget_show (GTK_WIDGET (menu));
+  gtk_widget_show (menu->toplevel);
   
   /* Find the last viewable ancestor, and make an X grab on it
    */
@@ -328,12 +415,22 @@ gtk_menu_popup (GtkMenu               *menu,
   if (xgrab_shell && (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab))
     {
       GdkCursor *cursor = gdk_cursor_new (GDK_ARROW);
-      
-      GTK_MENU_SHELL (xgrab_shell)->have_xgrab = 
-       (gdk_pointer_grab (xgrab_shell->window, TRUE,
-                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
-                          GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK,
-                          NULL, cursor, activate_time) == 0);
+
+      if ((gdk_pointer_grab (xgrab_shell->window, TRUE,
+                            GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+                            GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
+                            GDK_POINTER_MOTION_MASK,
+                            NULL, cursor, activate_time) == 0))
+       {
+         if (gdk_keyboard_grab (xgrab_shell->window, TRUE,
+                             activate_time) == 0)
+           GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
+         else
+           {
+             gdk_pointer_ungrab (activate_time);
+           }
+       }
+
       gdk_cursor_destroy (cursor);
     }
   
@@ -352,6 +449,7 @@ gtk_menu_popdown (GtkMenu *menu)
   
   menu_shell->parent_menu_shell = NULL;
   menu_shell->active = FALSE;
+  menu_shell->ignore_enter = FALSE;
   
   if (menu_shell->active_menu_item)
     {
@@ -362,9 +460,26 @@ gtk_menu_popdown (GtkMenu *menu)
   
   /* The X Grab, if present, will automatically be removed when we hide
    * the window */
-  gtk_widget_hide (GTK_WIDGET (menu));
+  gtk_widget_hide (menu->toplevel);
+
+  if (menu->torn_off)
+    {
+      if (GTK_BIN (menu->toplevel)->child)
+       gtk_widget_reparent (GTK_WIDGET (menu), menu->tearoff_window);
+      else
+       /* We popped up the menu from the tearoff, so we need to 
+        * release the grab - we aren't actually hiding the menu.
+        */
+       if (menu_shell->have_xgrab)
+         {
+           gdk_pointer_ungrab (GDK_CURRENT_TIME);
+           gdk_keyboard_ungrab (GDK_CURRENT_TIME);
+         }
+    }
+  else
+    gtk_widget_hide (GTK_WIDGET (menu));
+        
   menu_shell->have_xgrab = FALSE;
-  
   gtk_grab_remove (GTK_WIDGET (menu));
 }
 
@@ -435,99 +550,77 @@ gtk_menu_set_accel_group (GtkMenu *menu,
 }
 
 
-static void
-gtk_menu_show (GtkWidget *widget)
-{
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_MENU (widget));
-  
-  GTK_WIDGET_SET_FLAGS (widget, GTK_VISIBLE);
-  if (MENU_NEEDS_RESIZE (widget))
-    gtk_container_check_resize (GTK_CONTAINER (widget));
-  gtk_widget_map (widget);
-}
-
 void
 gtk_menu_reposition (GtkMenu *menu)
 {
-  GtkWidget *widget;
-
   g_return_if_fail (menu != NULL);
   g_return_if_fail (GTK_IS_MENU (menu));
 
-  widget = GTK_WIDGET (menu);
-  
-  if (GTK_WIDGET_DRAWABLE (menu))
-    {
-      gint x, y;
-      
-      gdk_window_get_pointer (NULL, &x, &y, NULL);
-      
-      if (menu->position_func)
-       (* menu->position_func) (menu, &x, &y, menu->position_func_data);
-      else
-       {
-         gint screen_width;
-         gint screen_height;
-         
-         screen_width = gdk_screen_width ();
-         screen_height = gdk_screen_height ();
-         
-         x -= 2;
-         y -= 2;
-         
-         if ((x + widget->requisition.width) > screen_width)
-           x -= ((x + widget->requisition.width) - screen_width);
-         if (x < 0)
-           x = 0;
-         if ((y + widget->requisition.height) > screen_height)
-           y -= ((y + widget->requisition.height) - screen_height);
-         if (y < 0)
-           y = 0;
-       }
-      
-      gdk_window_move (widget->window, x, y);
-    }
+  if (GTK_WIDGET_DRAWABLE (menu) && !menu->torn_off)
+    gtk_menu_position (menu);
 }
 
-static void
-gtk_menu_map (GtkWidget *widget)
+
+void       
+gtk_menu_set_tearoff_state (GtkMenu  *menu,
+                           gboolean  torn_off)
 {
-  GtkMenu *menu;
-  GtkMenuShell *menu_shell;
-  GtkWidget *child;
-  GList *children;
-  
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_MENU (widget));
-  
-  menu = GTK_MENU (widget);
-  menu_shell = GTK_MENU_SHELL (widget);
-  GTK_WIDGET_SET_FLAGS (menu_shell, GTK_MAPPED);
+  g_return_if_fail (menu != NULL);
+  g_return_if_fail (GTK_IS_MENU (menu));
 
-  gtk_menu_reposition (menu);
-  
-  children = menu_shell->children;
-  while (children)
+  if (menu->torn_off != torn_off)
     {
-      child = children->data;
-      children = children->next;
+      menu->torn_off = torn_off;
       
-      if (GTK_WIDGET_VISIBLE (child) && !GTK_WIDGET_MAPPED (child))
-       gtk_widget_map (child);
-    }
-  
-  gdk_window_show (widget->window);
-}
+      if (menu->torn_off)
+       {
+         if (GTK_WIDGET_VISIBLE (menu))
+           gtk_menu_popdown (menu);
 
-static void
-gtk_menu_unmap (GtkWidget *widget)
-{
-  g_return_if_fail (widget != NULL);
-  g_return_if_fail (GTK_IS_MENU (widget));
-  
-  GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
-  gdk_window_hide (widget->window);
+         if (!menu->tearoff_window)
+           {
+             GtkWidget *attach_widget;
+             
+             menu->tearoff_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+             gtk_signal_connect_object (GTK_OBJECT (menu->tearoff_window),  
+                                        "key_press_event",
+                                        GTK_SIGNAL_FUNC (gtk_menu_key_press), 
+                                        GTK_OBJECT (menu));
+             gtk_widget_realize (menu->tearoff_window);
+             
+             attach_widget = gtk_menu_get_attach_widget (menu);
+             if (GTK_IS_MENU_ITEM (attach_widget))
+               {
+                 GtkWidget *child = GTK_BIN (attach_widget)->child;
+                 if (GTK_IS_LABEL (child))
+                   {
+                     gchar *ret;
+                     gtk_label_get (GTK_LABEL (child), &ret);
+                     gdk_window_set_title (menu->tearoff_window->window, ret);
+                   }
+               }
+
+             gdk_window_set_decorations (menu->tearoff_window->window, 
+                                         GDK_DECOR_ALL |
+                                         GDK_DECOR_RESIZEH |
+                                         GDK_DECOR_MINIMIZE |
+                                         GDK_DECOR_MAXIMIZE);
+             gtk_window_set_policy (GTK_WINDOW (menu->tearoff_window),
+                                    FALSE, FALSE, TRUE);
+           }
+         gtk_widget_reparent (GTK_WIDGET (menu), menu->tearoff_window);
+
+         gtk_menu_position (menu);
+         
+         gtk_widget_show (GTK_WIDGET (menu));
+         gtk_widget_show (menu->tearoff_window);
+       }
+      else
+       {
+         gtk_widget_hide (menu->tearoff_window);
+         gtk_widget_reparent (GTK_WIDGET (menu), menu->toplevel);
+       }
+    }
 }
 
 static void
@@ -541,6 +634,7 @@ gtk_menu_realize (GtkWidget *widget)
   
   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
   
+  attributes.window_type = GDK_WINDOW_CHILD;
   attributes.x = widget->allocation.x;
   attributes.y = widget->allocation.y;
   attributes.width = widget->allocation.width;
@@ -548,14 +642,11 @@ gtk_menu_realize (GtkWidget *widget)
   attributes.wclass = GDK_INPUT_OUTPUT;
   attributes.visual = gtk_widget_get_visual (widget);
   attributes.colormap = gtk_widget_get_colormap (widget);
-  attributes.window_type = GDK_WINDOW_TEMP;
   attributes.event_mask = gtk_widget_get_events (widget);
-  attributes.event_mask |= (GDK_EXPOSURE_MASK |
-                           GDK_KEY_PRESS_MASK |
-                           GDK_STRUCTURE_MASK);
+  attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK);
   
   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
-  widget->window = gdk_window_new (NULL, &attributes, attributes_mask);
+  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
   gdk_window_set_user_data (widget->window, widget);
   
   widget->style = gtk_style_attach (widget->style, widget->window);
@@ -639,6 +730,11 @@ gtk_menu_size_allocate (GtkWidget     *widget,
   menu_shell = GTK_MENU_SHELL (widget);
   
   widget->allocation = *allocation;
+  if (GTK_WIDGET_REALIZED (widget))
+    gdk_window_move_resize (widget->window,
+                           allocation->x, allocation->y,
+                           allocation->width, allocation->height);
+
 
   if (menu_shell->children)
     {
@@ -665,13 +761,6 @@ gtk_menu_size_allocate (GtkWidget     *widget,
            }
        }
     }
-
-  if (GTK_WIDGET_REALIZED (widget))
-    {
-      gdk_window_resize (widget->window,
-                        widget->requisition.width,
-                        widget->requisition.height);
-    }
 }
 
 static void
@@ -759,55 +848,35 @@ gtk_menu_expose (GtkWidget        *widget,
 }
 
 static gint
-gtk_menu_configure (GtkWidget        *widget,
-                   GdkEventConfigure *event)
+gtk_menu_key_press (GtkWidget  *widget,
+                   GdkEventKey *event)
 {
-  GtkAllocation allocation;
+  GtkMenuShell *menu_shell;
+  gboolean delete = FALSE;
   
   g_return_val_if_fail (widget != NULL, FALSE);
   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
   g_return_val_if_fail (event != NULL, FALSE);
-  
-  /* If the window was merely moved, do nothing */
-  if ((widget->allocation.width == event->width) &&
-      (widget->allocation.height == event->height))
-    return FALSE;
-  
-  if (MENU_NEEDS_RESIZE (widget))
-    {
-      MENU_NEEDS_RESIZE (widget) = FALSE;
       
-      allocation.x = 0;
-      allocation.y = 0;
-      allocation.width = event->width;
-      allocation.height = event->height;
-      
-      gtk_widget_size_allocate (widget, &allocation);
+  menu_shell = GTK_MENU_SHELL (widget);
+
+  if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
+    return TRUE;
+
+  switch (event->keyval)
+    {
+    case GDK_Delete:
+    case GDK_KP_Delete:
+    case GDK_BackSpace:
+      delete = TRUE;
+      break;
+    default:
+      break;
     }
-  
-  return FALSE;
-}
 
-static gint
-gtk_menu_key_press (GtkWidget  *widget,
-                   GdkEventKey *event)
-{
-  gboolean delete;
-  
-  g_return_val_if_fail (widget != NULL, FALSE);
-  g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
-  g_return_val_if_fail (event != NULL, FALSE);
-  
-  delete = (event->keyval == GDK_Delete ||
-           event->keyval == GDK_KP_Delete ||
-           event->keyval == GDK_BackSpace);
-  
+  /* Modify the accelerators */
   if (delete || gtk_accelerator_valid (event->keyval, event->keyval))
     {
-      GtkMenuShell *menu_shell;
-      
-      menu_shell = GTK_MENU_SHELL (widget);
-      
       if (menu_shell->active_menu_item &&
          GTK_BIN (menu_shell->active_menu_item)->child &&
          GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
@@ -861,32 +930,27 @@ gtk_menu_key_press (GtkWidget     *widget,
   return FALSE;
 }
 
-static void
-gtk_menu_check_resize (GtkContainer *container)
+static gint 
+gtk_menu_motion_notify  (GtkWidget        *widget,
+                        GdkEventMotion    *event)
 {
-  GtkAllocation allocation;
-  GtkWidget *widget;
-  
-  g_return_if_fail (container != NULL);
-  g_return_if_fail (GTK_IS_MENU (container));
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
 
-  widget = GTK_WIDGET (container);
-  
-  if (GTK_WIDGET_VISIBLE (container))
+  if (GTK_MENU_SHELL (widget)->ignore_enter)
+    GTK_MENU_SHELL (widget)->ignore_enter = FALSE;
+  else
     {
-      MENU_NEEDS_RESIZE (container) = FALSE;
-      
-      gtk_widget_size_request (widget, &widget->requisition);
-      
-      allocation.x = widget->allocation.x;
-      allocation.y = widget->allocation.y;
-      allocation.width = widget->requisition.width;
-      allocation.height = widget->requisition.height;
-      
-      gtk_widget_size_allocate (widget, &allocation);
+      GdkEvent send_event;
+
+      send_event.crossing.type = GDK_ENTER_NOTIFY;
+      send_event.crossing.window = event->window;
+      send_event.crossing.time = event->time;
+
+      gtk_widget_event (widget, &send_event);
     }
-  else
-    MENU_NEEDS_RESIZE (container) = TRUE;
+      
+  return FALSE;
 }
 
 static void
@@ -933,3 +997,51 @@ gtk_menu_hide_all (GtkWidget *widget)
   /* Hide children, but not self. */
   gtk_container_foreach (container, (GtkCallback) gtk_widget_hide_all, NULL);
 }
+
+static void
+gtk_menu_position (GtkMenu *menu)
+{
+  GtkWidget *widget;
+  gint x, y;
+  g_return_if_fail (menu != NULL);
+  g_return_if_fail (GTK_IS_MENU (menu));
+
+  widget = GTK_WIDGET (menu);
+
+  gdk_window_get_pointer (NULL, &x, &y, NULL);
+
+  /* We need the requisition to figure out the right place to
+   * popup the menu. In fact, we always need to ask here, since
+   * if one a size_request was queued while we weren't popped up,
+   * the requisition won't have been recomputed yet.
+   */
+  gtk_widget_size_request (widget, &widget->requisition);
+      
+  if (menu->position_func)
+    (* menu->position_func) (menu, &x, &y, menu->position_func_data);
+  else
+    {
+      gint screen_width;
+      gint screen_height;
+      
+      screen_width = gdk_screen_width ();
+      screen_height = gdk_screen_height ();
+         
+      x -= 2;
+      y -= 2;
+      
+      if ((x + widget->requisition.width) > screen_width)
+       x -= ((x + widget->requisition.width) - screen_width);
+      if (x < 0)
+       x = 0;
+      if ((y + widget->requisition.height) > screen_height)
+       y -= ((y + widget->requisition.height) - screen_height);
+      if (y < 0)
+       y = 0;
+    }
+  
+  gtk_widget_set_uposition (GTK_MENU_SHELL (menu)->active ?
+                               menu->toplevel : menu->tearoff_window, 
+                           x, y);
+}
index ac47f54bafb1af480bee21361d879e26f86cd9e8..8600627dd0796690dc1b3538ed69ee029ba73229 100644 (file)
@@ -58,6 +58,11 @@ struct _GtkMenu
   GtkAccelGroup *accel_group;
   GtkMenuPositionFunc position_func;
   gpointer position_func_data;
+
+  GtkWidget *toplevel;
+  GtkWidget *tearoff_window;
+
+  guint torn_off : 1;
 };
 
 struct _GtkMenuClass
@@ -94,7 +99,8 @@ void     gtk_menu_attach_to_widget      (GtkMenu             *menu,
                                           GtkMenuDetachFunc    detacher);
 GtkWidget* gtk_menu_get_attach_widget    (GtkMenu             *menu);
 void      gtk_menu_detach                (GtkMenu             *menu);
-
+void       gtk_menu_set_tearoff_state    (GtkMenu              *menu,
+                                         gboolean              torn_off);
 
 #ifdef __cplusplus
 }
index 1983e3dd4a8406981a4e51c2109cb5e17e147d9c..3f04e5193b02bd0af58ecf8a0d90ecb25e13fa6a 100644 (file)
@@ -16,6 +16,8 @@
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
+#include "gdk/gdkkeysyms.h"
+#include "gtkbindings.h"
 #include "gtkmain.h"
 #include "gtkmenubar.h"
 #include "gtkmenuitem.h"
@@ -69,6 +71,8 @@ gtk_menu_bar_class_init (GtkMenuBarClass *class)
   GtkWidgetClass *widget_class;
   GtkMenuShellClass *menu_shell_class;
 
+  GtkBindingSet *binding_set;
+
   widget_class = (GtkWidgetClass*) class;
   menu_shell_class = (GtkMenuShellClass*) class;
 
@@ -78,6 +82,28 @@ gtk_menu_bar_class_init (GtkMenuBarClass *class)
   widget_class->expose_event = gtk_menu_bar_expose;
 
   menu_shell_class->submenu_placement = GTK_TOP_BOTTOM;
+
+  binding_set = gtk_binding_set_by_class (class);
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_Left, 0,
+                               "move_current", 1,
+                               GTK_TYPE_MENU_DIRECTION_TYPE,
+                               GTK_MENU_DIR_PREV);
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_Right, 0,
+                               "move_current", 1,
+                               GTK_TYPE_MENU_DIRECTION_TYPE,
+                               GTK_MENU_DIR_NEXT);
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_Up, 0,
+                               "move_current", 1,
+                               GTK_TYPE_MENU_DIRECTION_TYPE,
+                               GTK_MENU_DIR_PARENT);
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_Down, 0,
+                               "move_current", 1,
+                               GTK_TYPE_MENU_DIRECTION_TYPE,
+                               GTK_MENU_DIR_CHILD);
 }
 
 static void
index e4bf0f0f96afd151b25301b0e5ebbc96d393bd7b..ba79c57db6633cabfcf0b28c3cd2bd848828f832 100644 (file)
@@ -20,6 +20,7 @@
 #include "gtkaccellabel.h"
 #include "gtkmain.h"
 #include "gtkmenu.h"
+#include "gtkmenubar.h"
 #include "gtkmenuitem.h"
 #include "gtksignal.h"
 
@@ -32,6 +33,7 @@
 
 enum {
   ACTIVATE,
+  ACTIVATE_ITEM,
   LAST_SIGNAL
 };
 
@@ -51,6 +53,7 @@ static gint gtk_menu_item_expose         (GtkWidget        *widget,
                                          GdkEventExpose   *event);
 static void gtk_real_menu_item_select    (GtkItem          *item);
 static void gtk_real_menu_item_deselect  (GtkItem          *item);
+static void gtk_real_menu_item_activate_item  (GtkMenuItem      *item);
 static gint gtk_menu_item_select_timeout (gpointer          data);
 static void gtk_menu_item_position_menu  (GtkMenu          *menu,
                                          gint             *x,
@@ -110,6 +113,14 @@ gtk_menu_item_class_init (GtkMenuItemClass *klass)
                     gtk_marshal_NONE__NONE,
                    GTK_TYPE_NONE, 0);
 
+  menu_item_signals[ACTIVATE_ITEM] =
+    gtk_signal_new ("activate_item",
+                    GTK_RUN_FIRST,
+                    object_class->type,
+                    GTK_SIGNAL_OFFSET (GtkMenuItemClass, activate_item),
+                    gtk_signal_default_marshaller,
+                   GTK_TYPE_NONE, 0);
+
   gtk_object_class_add_signals (object_class, menu_item_signals, LAST_SIGNAL);
 
   object_class->destroy = gtk_menu_item_destroy;
@@ -126,8 +137,10 @@ gtk_menu_item_class_init (GtkMenuItemClass *klass)
   item_class->deselect = gtk_real_menu_item_deselect;
 
   klass->activate = NULL;
+  klass->activate_item = gtk_real_menu_item_activate_item;
 
   klass->toggle_size = 0;
+  klass->hide_on_activate = TRUE;
 }
 
 static void
@@ -493,8 +506,16 @@ gtk_real_menu_item_select (GtkItem *item)
 
   menu_item = GTK_MENU_ITEM (item);
 
-  if (menu_item->submenu && !GTK_WIDGET_VISIBLE (menu_item->submenu))
-    menu_item->timer = gtk_timeout_add (SELECT_TIMEOUT, gtk_menu_item_select_timeout, menu_item);
+  /*  if (menu_item->submenu && !GTK_WIDGET_VISIBLE (menu_item->submenu))*/
+  if (menu_item->submenu)
+    {
+  /* Boy this is a hack! */
+      GdkEvent *current_event = gtk_get_current_event();
+      if (current_event && (current_event->type != GDK_ENTER_NOTIFY))
+       gtk_menu_item_select_timeout (menu_item);
+      else
+       menu_item->timer = gtk_timeout_add (SELECT_TIMEOUT, gtk_menu_item_select_timeout, menu_item);
+    }
 
   gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_PRELIGHT);
   gtk_widget_draw (GTK_WIDGET (menu_item), NULL);
@@ -522,6 +543,38 @@ gtk_real_menu_item_deselect (GtkItem *item)
   gtk_widget_draw (GTK_WIDGET (menu_item), NULL);
 }
 
+static void
+gtk_real_menu_item_activate_item (GtkMenuItem *menu_item)
+{
+  GtkWidget *widget;
+
+  g_return_if_fail (menu_item != NULL);
+  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+
+  widget = GTK_WIDGET (menu_item);
+  
+  if (widget->parent &&
+      GTK_IS_MENU_SHELL (widget->parent))
+    {
+      if (menu_item->submenu == NULL)
+       gtk_menu_shell_activate_item (GTK_MENU_SHELL (widget->parent),
+                                     widget, TRUE);
+      else
+       {
+         GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget->parent);
+
+         if (!menu_shell->active)
+           {
+             gtk_grab_add (GTK_WIDGET (menu_shell));
+             menu_shell->have_grab = TRUE;
+             menu_shell->active = TRUE;
+           }
+
+         gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent), widget);
+       }
+    }
+}
+
 static gint
 gtk_menu_item_select_timeout (gpointer data)
 {
@@ -538,6 +591,16 @@ gtk_menu_item_select_timeout (gpointer data)
                  GTK_MENU_SHELL (GTK_WIDGET (menu_item)->parent)->button,
                  0);
 
+  /* This is a bit of a hack - we want to select the first item
+   * of menus hanging of a menu bar, but not for cascading submenus
+   */
+  if (GTK_IS_MENU_BAR (GTK_WIDGET (menu_item)->parent))
+    {
+      GtkMenuShell *submenu = GTK_MENU_SHELL (menu_item->submenu);
+      if (submenu->children)
+       gtk_menu_shell_select_item (submenu, submenu->children->data);
+    }
+
   return FALSE;
 }
 
index 82a9244fe0a3a4517022085a9cadc8567b95bc79..fdfafed310616b8ba3bd6263a6c47fcea0f18d45 100644 (file)
@@ -63,8 +63,16 @@ struct _GtkMenuItemClass
   GtkItemClass parent_class;
   
   guint toggle_size;
+  /* If the following flag is true, then we should always hide
+   * the menu when the MenuItem is activated. Otherwise, the 
+   * it is up to the caller. For instance, when navigating
+   * a menu with the keyboard, <Space> doesn't hide, but
+   * <Return> does.
+   */
+  guint hide_on_activate : 1;
   
-  void (* activate) (GtkMenuItem *menu_item);
+  void (* activate)      (GtkMenuItem *menu_item);
+  void (* activate_item) (GtkMenuItem *menu_item);
 };
 
 
index d37155d5dad08944e5c222817f732466192cfd42..333e1c12978887ee8267437dd71bef1d317b0801 100644 (file)
  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  * Boston, MA 02111-1307, USA.
  */
+#include "gdk/gdkkeysyms.h"
+#include "gtkbindings.h"
 #include "gtkmain.h"
 #include "gtkmenuitem.h"
+#include "gtktearoffmenuitem.h" /* FIXME */
 #include "gtkmenushell.h"
 #include "gtksignal.h"
 
 enum {
   DEACTIVATE,
   SELECTION_DONE,
+  MOVE_CURRENT,
+  ACTIVATE_CURRENT,
+  CANCEL,
   LAST_SIGNAL
 };
 
+typedef void (*GtkMenuShellSignal1) (GtkObject           *object,
+                                    GtkMenuDirectionType arg1,
+                                    gpointer             data);
+typedef void (*GtkMenuShellSignal2) (GtkObject *object,
+                                    gboolean   arg1,
+                                    gpointer   data);
+
+/* Terminology:
+ * 
+ * A menu item can be "selected", this means that it is displayed
+ * in the prelight state, and if it has a submenu, that submenu
+ * will be popped up. 
+ * 
+ * A menu is "active" when it is visible onscreen and the user
+ * is selecting from it. A menubar is not active until the user
+ * clicks on one of its menuitems. When a menu is active,
+ * passing the mouse over a submenu will pop it up.
+ *
+ * menu_shell->active_menu_item, is however, not an "active"
+ * menu item (there is no such thing) but rather, the selected
+ * menu item in that MenuShell, if there is one.
+ *
+ * There is also is a concept of the current menu and a current
+ * menu item. The current menu item is the selected menu item
+ * that is furthest down in the heirarchy. (Every active menu_shell
+ * does not necessarily contain a selected menu item, but if
+ * it does, then menu_shell->parent_menu_shell must also contain
+ * a selected menu item. The current menu is the menu that 
+ * contains the current menu_item. It will always have a GTK
+ * grab and receive all key presses.
+ *
+ *
+ * Action signals:
+ *
+ *  ::move_current (GtkMenuDirection *dir)
+ *     Moves the current menu item in direction 'dir':
+ *
+ *       GTK_MENU_DIR_PARENT: To the parent menu shell
+ *       GTK_MENU_DIR_CHILD: To the child menu shell (if this item has
+ *          a submenu.
+ *       GTK_MENU_DIR_NEXT/PREV: To the next or previous item
+ *          in this menu.
+ * 
+ *     As a a bit of a hack to get movement between menus and
+ *     menubars working, if submenu_placement is different for
+ *     the menu and its MenuShell then the following apply:
+ * 
+ *       - For 'parent' the current menu is not just moved to
+ *         the parent, but moved to the previous entry in the parent
+ *       - For 'child', if there is no child, then current is
+ *         moved to the next item in the parent.
+ *
+ * 
+ *  ::activate_current (GBoolean *force_hide)
+ *     Activate the current item. If 'force_hide' is true, hide
+ *     the current menu item always. Otherwise, only hide
+ *     it if menu_item->klass->hide_on_activate is true.
+ *
+ *  ::cancel ()
+ *     Cancels the current selection
+ */
 
 static void gtk_menu_shell_class_init        (GtkMenuShellClass *klass);
 static void gtk_menu_shell_init              (GtkMenuShell      *menu_shell);
@@ -41,6 +108,8 @@ static gint gtk_menu_shell_button_press      (GtkWidget         *widget,
                                              GdkEventButton    *event);
 static gint gtk_menu_shell_button_release    (GtkWidget         *widget,
                                              GdkEventButton    *event);
+static gint gtk_menu_shell_key_press         (GtkWidget                *widget,
+                                             GdkEventKey       *event);
 static gint gtk_menu_shell_enter_notify      (GtkWidget         *widget,
                                              GdkEventCrossing  *event);
 static gint gtk_menu_shell_leave_notify      (GtkWidget         *widget,
@@ -57,8 +126,14 @@ static gint gtk_menu_shell_is_item           (GtkMenuShell      *menu_shell,
                                              GtkWidget         *child);
 static GtkWidget *gtk_menu_shell_get_item    (GtkMenuShell      *menu_shell,
                                              GdkEvent          *event);
-static GtkType gtk_menu_shell_child_type     (GtkContainer      *container);
+static GtkType    gtk_menu_shell_child_type  (GtkContainer      *container);
 
+static void       gtk_menu_shell_deselect    (GtkMenuShell      *menu_shell);
+static void gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
+                                             GtkMenuDirectionType direction);
+static void gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
+                                                 gboolean           force_hide);
+static void gtk_real_menu_shell_cancel           (GtkMenuShell      *menu_shell);
 
 static GtkContainerClass *parent_class = NULL;
 static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
@@ -96,6 +171,8 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass)
   GtkWidgetClass *widget_class;
   GtkContainerClass *container_class;
 
+  GtkBindingSet *binding_set;
+
   object_class = (GtkObjectClass*) klass;
   widget_class = (GtkWidgetClass*) klass;
   container_class = (GtkContainerClass*) klass;
@@ -116,12 +193,37 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass)
                     GTK_SIGNAL_OFFSET (GtkMenuShellClass, selection_done),
                     gtk_marshal_NONE__NONE,
                    GTK_TYPE_NONE, 0);
+  menu_shell_signals[MOVE_CURRENT] =
+    gtk_signal_new ("move_current",
+                   GTK_RUN_LAST | GTK_RUN_ACTION,
+                   object_class->type,
+                    GTK_SIGNAL_OFFSET (GtkMenuShellClass, move_current),
+                   gtk_marshal_NONE__ENUM,
+                   GTK_TYPE_NONE, 1, 
+                   GTK_TYPE_MENU_DIRECTION_TYPE);
+  menu_shell_signals[ACTIVATE_CURRENT] =
+    gtk_signal_new ("activate_current",
+                   GTK_RUN_LAST | GTK_RUN_ACTION,
+                   object_class->type,
+                    GTK_SIGNAL_OFFSET (GtkMenuShellClass, activate_current),
+                   gtk_marshal_NONE__BOOL,
+                   GTK_TYPE_NONE, 1, 
+                   GTK_TYPE_BOOL);
+  menu_shell_signals[CANCEL] =
+    gtk_signal_new ("cancel",
+                   GTK_RUN_LAST | GTK_RUN_ACTION,
+                   object_class->type,
+                    GTK_SIGNAL_OFFSET (GtkMenuShellClass, cancel),
+                    gtk_marshal_NONE__NONE,
+                   GTK_TYPE_NONE, 0);
+  
   gtk_object_class_add_signals (object_class, menu_shell_signals, LAST_SIGNAL);
 
   widget_class->map = gtk_menu_shell_map;
   widget_class->realize = gtk_menu_shell_realize;
   widget_class->button_press_event = gtk_menu_shell_button_press;
   widget_class->button_release_event = gtk_menu_shell_button_release;
+  widget_class->key_press_event = gtk_menu_shell_key_press;
   widget_class->enter_notify_event = gtk_menu_shell_enter_notify;
   widget_class->leave_notify_event = gtk_menu_shell_leave_notify;
 
@@ -133,6 +235,24 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass)
   klass->submenu_placement = GTK_TOP_BOTTOM;
   klass->deactivate = gtk_real_menu_shell_deactivate;
   klass->selection_done = NULL;
+  klass->move_current = gtk_real_menu_shell_move_current;
+  klass->activate_current = gtk_real_menu_shell_activate_current;
+  klass->cancel = gtk_real_menu_shell_cancel;
+
+  binding_set = gtk_binding_set_by_class (klass);
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_Escape, 0,
+                               "cancel", 0);
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_Return, 0,
+                               "activate_current", 1,
+                               GTK_TYPE_BOOL,
+                               TRUE);
+  gtk_binding_entry_add_signal (binding_set,
+                               GDK_space, 0,
+                               "activate_current", 1,
+                               GTK_TYPE_BOOL,
+                               FALSE);
 }
 
 static GtkType
@@ -282,6 +402,7 @@ gtk_menu_shell_realize (GtkWidget *widget)
   attributes.event_mask |= (GDK_EXPOSURE_MASK |
                            GDK_BUTTON_PRESS_MASK |
                            GDK_BUTTON_RELEASE_MASK |
+                           GDK_KEY_PRESS_MASK |
                            GDK_ENTER_NOTIFY_MASK |
                            GDK_LEAVE_NOTIFY_MASK);
 
@@ -388,16 +509,7 @@ gtk_menu_shell_button_release (GtkWidget      *widget,
            {
              if (GTK_MENU_ITEM (menu_item)->submenu == NULL)
                {
-                 gtk_menu_shell_deactivate (menu_shell);
-
-                 /* flush the x-queue, so any grabs are removed and
-                  * the menu is actually taken down
-                  */
-                 gdk_flush ();
-                 gtk_widget_activate (menu_item);
-
-                 gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
-                 
+                 gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE);
                  return TRUE;
                }
            }
@@ -433,6 +545,32 @@ gtk_menu_shell_button_release (GtkWidget      *widget,
   return TRUE;
 }
 
+static gint
+gtk_menu_shell_key_press (GtkWidget    *widget,
+                         GdkEventKey *event)
+{
+  GtkMenuShell *menu_shell;
+  
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+      
+  menu_shell = GTK_MENU_SHELL (widget);
+
+  if (!menu_shell->active_menu_item && menu_shell->parent_menu_shell)
+    return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent *)event);
+  
+  if (gtk_bindings_activate (GTK_OBJECT (widget),
+                            event->keyval,
+                            event->state))
+    return TRUE;
+
+  if (gtk_accel_groups_activate (GTK_OBJECT (widget), event->keyval, event->state))
+    return TRUE;
+
+  return FALSE;
+}
+
 static gint
 gtk_menu_shell_enter_notify (GtkWidget        *widget,
                             GdkEventCrossing *event)
@@ -445,7 +583,8 @@ gtk_menu_shell_enter_notify (GtkWidget        *widget,
   g_return_val_if_fail (event != NULL, FALSE);
 
   menu_shell = GTK_MENU_SHELL (widget);
-  if (menu_shell->active)
+
+  if (menu_shell->active && !menu_shell->ignore_enter)
     {
       menu_item = gtk_get_event_widget ((GdkEvent*) event);
 
@@ -459,15 +598,7 @@ gtk_menu_shell_enter_notify (GtkWidget        *widget,
          if ((event->detail != GDK_NOTIFY_INFERIOR) &&
              (GTK_WIDGET_STATE (menu_item) != GTK_STATE_PRELIGHT))
            {
-             if (menu_shell->active_menu_item)
-               gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
-
-             menu_shell->active_menu_item = menu_item;
-             gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
-                                          MENU_SHELL_CLASS (menu_shell)->submenu_placement);
-             gtk_menu_item_select (GTK_MENU_ITEM (menu_shell->active_menu_item));
-             if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
-               gtk_widget_activate (menu_shell->active_menu_item);
+             gtk_menu_shell_select_item (menu_shell, menu_item);
            }
        }
       else if (menu_shell->parent_menu_shell)
@@ -516,8 +647,7 @@ gtk_menu_shell_leave_notify (GtkWidget        *widget,
          if ((event->detail != GDK_NOTIFY_INFERIOR) &&
              (GTK_WIDGET_STATE (menu_item) != GTK_STATE_NORMAL))
            {
-             gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
-             menu_shell->active_menu_item = NULL;
+             gtk_menu_shell_deselect (menu_shell);
            }
        }
       else if (menu_shell->parent_menu_shell)
@@ -613,6 +743,7 @@ gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
        {
          menu_shell->have_xgrab = FALSE;
          gdk_pointer_ungrab (GDK_CURRENT_TIME);
+         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
        }
     }
 }
@@ -655,3 +786,181 @@ gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
     return NULL;
 }
 
+/* Handlers for action signals */
+
+void
+gtk_menu_shell_select_item (GtkMenuShell      *menu_shell,
+                           GtkWidget         *menu_item)
+{
+  g_return_if_fail (menu_shell != NULL);
+  g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
+  g_return_if_fail (menu_item != NULL);
+  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+
+  if (menu_shell->active_menu_item)
+    gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
+  
+  menu_shell->active_menu_item = menu_item;
+  gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
+                              MENU_SHELL_CLASS (menu_shell)->submenu_placement);
+  gtk_menu_item_select (GTK_MENU_ITEM (menu_shell->active_menu_item));
+
+  /* This allows the bizarre radio buttons-with-submenus-display-history
+   * behavior
+   */
+  if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
+    gtk_widget_activate (menu_shell->active_menu_item);
+}
+
+static void
+gtk_menu_shell_deselect (GtkMenuShell      *menu_shell)
+{
+  gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
+  menu_shell->active_menu_item = NULL;
+}
+
+void
+gtk_menu_shell_activate_item (GtkMenuShell      *menu_shell,
+                             GtkWidget         *menu_item,
+                             gboolean           force_deactivate)
+{
+  gboolean deactivate = force_deactivate;
+
+  g_return_if_fail (menu_shell != NULL);
+  g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
+  g_return_if_fail (menu_item != NULL);
+  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
+
+  if (!deactivate)
+    {
+      deactivate = GTK_MENU_ITEM_CLASS (GTK_OBJECT (menu_item)->klass)->hide_on_activate;
+    }
+
+  if (deactivate)
+    {
+      gtk_menu_shell_deactivate (menu_shell);
+  
+      /* flush the x-queue, so any grabs are removed and
+       * the menu is actually taken down
+       */
+      gdk_flush ();
+    }
+  gtk_widget_activate (menu_item);
+
+  if (deactivate)
+    gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
+}
+
+/* Distance should be +/- 1 */
+static void
+gtk_menu_shell_move_selected (GtkMenuShell  *menu_shell, 
+                             gint           distance)
+{
+  if (menu_shell->active_menu_item)
+    {
+      GList *node = g_list_find (menu_shell->children,
+                                menu_shell->active_menu_item);
+      GList *start_node = node;
+      
+      if (distance > 0)
+       {
+         node = node->next;
+         while (node != start_node && 
+                (!node || !GTK_WIDGET_SENSITIVE (node->data)))
+           {
+             if (!node)
+               node = menu_shell->children;
+             else
+               node = node->next;
+           }
+       }
+      else
+       {
+         node = node->prev;
+         while (node != start_node &&
+                (!node || !GTK_WIDGET_SENSITIVE (node->data)))
+           {
+             if (!node)
+               node = g_list_last (menu_shell->children);
+             else
+               node = node->prev;
+           }
+       }
+      
+      if (node)
+       gtk_menu_shell_select_item (menu_shell, node->data);
+    }
+}
+
+static void
+gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
+                                 GtkMenuDirectionType direction)
+{
+  GtkMenuShell *parent_menu_shell = NULL;
+
+  if (menu_shell->parent_menu_shell)
+    parent_menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
+  
+  switch (direction)
+    {
+    case GTK_MENU_DIR_PARENT:
+      if (parent_menu_shell)
+       {
+         if (GTK_MENU_SHELL_CLASS (GTK_OBJECT (parent_menu_shell)->klass)->submenu_placement == 
+                      GTK_MENU_SHELL_CLASS (GTK_OBJECT (menu_shell)->klass)->submenu_placement)
+           gtk_menu_shell_deselect (menu_shell);
+         else
+           gtk_menu_shell_move_selected (parent_menu_shell, -1);
+       }
+      break;
+      
+    case GTK_MENU_DIR_CHILD:
+      if (GTK_BIN (menu_shell->active_menu_item)->child &&
+         GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
+       {
+         menu_shell = GTK_MENU_SHELL (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu);
+         if (menu_shell->children)
+           gtk_menu_shell_select_item (menu_shell, menu_shell->children->data);
+       }
+      else
+       {
+         /* Try to find a menu running the opposite direction */
+         while (parent_menu_shell && 
+                (GTK_MENU_SHELL_CLASS (GTK_OBJECT (parent_menu_shell)->klass)->submenu_placement ==
+                 GTK_MENU_SHELL_CLASS (GTK_OBJECT (menu_shell)->klass)->submenu_placement))
+           parent_menu_shell = GTK_MENU_SHELL (parent_menu_shell->parent_menu_shell);
+         
+         if (parent_menu_shell)
+           gtk_menu_shell_move_selected (parent_menu_shell, 1);
+       }
+      break;
+
+    case GTK_MENU_DIR_PREV:
+      gtk_menu_shell_move_selected (menu_shell, -1);
+      break;
+    case GTK_MENU_DIR_NEXT:
+      gtk_menu_shell_move_selected (menu_shell, 1);
+      break;
+    }
+}
+
+static void
+gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
+                                     gboolean           force_hide)
+{
+  if (menu_shell->active_menu_item &&
+      GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
+    {
+      gtk_menu_shell_activate_item (menu_shell,
+                                   menu_shell->active_menu_item,
+                                   force_hide);
+    }
+}
+
+static void
+gtk_real_menu_shell_cancel (GtkMenuShell      *menu_shell)
+{
+  gtk_menu_shell_deactivate (menu_shell);
+  gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
+}
+
index 808e50ab81e845d7add1353f5a72aa5f61d1a96b..965551e31c20fbd2271be56f84dcb80b8807eb57 100644 (file)
@@ -54,6 +54,7 @@ struct _GtkMenuShell
   guint button : 2;
   guint ignore_leave : 1;
   guint menu_flag : 1;
+  guint ignore_enter : 1;
   
   guint32 activate_time;
 };
@@ -66,6 +67,12 @@ struct _GtkMenuShellClass
   
   void (*deactivate)     (GtkMenuShell *menu_shell);
   void (*selection_done) (GtkMenuShell *menu_shell);
+
+  void (*move_current)     (GtkMenuShell        *menu_shell,
+                           GtkMenuDirectionType direction);
+  void (*activate_current) (GtkMenuShell *menu_shell,
+                           gboolean      force_hide);
+  void (*cancel)           (GtkMenuShell *menu_shell);
 };
 
 
@@ -78,6 +85,11 @@ void gtk_menu_shell_insert     (GtkMenuShell *menu_shell,
                                   GtkWidget    *child,
                                   gint          position);
 void   gtk_menu_shell_deactivate (GtkMenuShell *menu_shell);
+void    gtk_menu_shell_select_item (GtkMenuShell      *menu_shell,
+                                   GtkWidget         *menu_item);
+void    gtk_menu_shell_activate_item  (GtkMenuShell      *menu_shell,
+                                      GtkWidget         *menu_item,
+                                      gboolean           force_deactivate);
 
 
 #ifdef __cplusplus
diff --git a/gtk/gtktearoffmenuitem.c b/gtk/gtktearoffmenuitem.c
new file mode 100644 (file)
index 0000000..432754d
--- /dev/null
@@ -0,0 +1,250 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "gtkmenu.h"
+#include "gtksignal.h"
+#include "gtktearoffmenuitem.h"
+
+#define ARROW_SIZE 10
+#define TEAR_LENGTH 5
+#define BORDER_SPACING  3
+
+static void gtk_tearoff_menu_item_class_init (GtkTearoffMenuItemClass *klass);
+static void gtk_tearoff_menu_item_init       (GtkTearoffMenuItem      *tearoff_menu_item);
+static void gtk_tearoff_menu_item_size_request (GtkWidget             *widget,
+                                               GtkRequisition        *requisition);
+static void gtk_tearoff_menu_item_draw       (GtkWidget             *widget,
+                                             GdkRectangle          *area);
+static gint gtk_tearoff_menu_item_expose     (GtkWidget             *widget,
+                                             GdkEventExpose        *event);
+static void gtk_tearoff_menu_item_activate   (GtkMenuItem           *menu_item);
+static gint gtk_tearoff_menu_item_delete_cb  (GtkMenuItem           *menu_item,
+                                             GdkEventAny           *event);
+
+GtkType
+gtk_tearoff_menu_item_get_type (void)
+{
+  static GtkType tearoff_menu_item_type = 0;
+
+  if (!tearoff_menu_item_type)
+    {
+      GtkTypeInfo tearoff_menu_item_info =
+      {
+        "GtkTearoffMenuItem",
+        sizeof (GtkTearoffMenuItem),
+        sizeof (GtkTearoffMenuItemClass),
+        (GtkClassInitFunc) gtk_tearoff_menu_item_class_init,
+        (GtkObjectInitFunc) gtk_tearoff_menu_item_init,
+        /* reserved_1 */ NULL,
+        /* reserved_2 */ NULL,
+        (GtkClassInitFunc) NULL,
+      };
+
+      tearoff_menu_item_type = gtk_type_unique (gtk_menu_item_get_type (), &tearoff_menu_item_info);
+    }
+
+  return tearoff_menu_item_type;
+}
+
+GtkWidget*
+gtk_tearoff_menu_item_new (void)
+{
+  return GTK_WIDGET (gtk_type_new (gtk_tearoff_menu_item_get_type ()));
+}
+
+static void
+gtk_tearoff_menu_item_class_init (GtkTearoffMenuItemClass *klass)
+{
+  GtkObjectClass *object_class;
+  GtkWidgetClass *widget_class;
+  GtkMenuItemClass *menu_item_class;
+
+  object_class = (GtkObjectClass*) klass;
+  widget_class = (GtkWidgetClass*) klass;
+  menu_item_class = (GtkMenuItemClass*) klass;
+
+  widget_class->draw = gtk_tearoff_menu_item_draw;
+  widget_class->expose_event = gtk_tearoff_menu_item_expose;
+  widget_class->size_request = gtk_tearoff_menu_item_size_request;
+
+  menu_item_class->activate = gtk_tearoff_menu_item_activate;
+}
+
+static void
+gtk_tearoff_menu_item_init (GtkTearoffMenuItem *tearoff_menu_item)
+{
+  tearoff_menu_item->torn_off = FALSE;
+}
+
+static void
+gtk_tearoff_menu_item_size_request (GtkWidget      *widget,
+                                   GtkRequisition *requisition)
+{
+  GtkTearoffMenuItem *tearoff;
+
+  tearoff = GTK_TEAROFF_MENU_ITEM (widget);
+  
+  requisition->width = (GTK_CONTAINER (widget)->border_width +
+                       widget->style->klass->xthickness +
+                       BORDER_SPACING) * 2;
+  requisition->height = (GTK_CONTAINER (widget)->border_width +
+                        widget->style->klass->ythickness) * 2;
+
+  if (tearoff->torn_off)
+    {
+      requisition->height += ARROW_SIZE;
+    }
+  else
+    {
+      requisition->height += widget->style->klass->ythickness;
+    }
+}
+
+static void
+gtk_tearoff_menu_item_paint (GtkWidget   *widget,
+                            GdkRectangle *area)
+{
+  GtkMenuItem *menu_item;
+  GtkTearoffMenuItem *tearoff_item;
+  GtkShadowType shadow_type;
+  gint width, height;
+  gint x, y;
+  gint right_max;
+
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (GTK_IS_TEAROFF_MENU_ITEM (widget));
+
+  if (GTK_WIDGET_DRAWABLE (widget))
+    {
+      menu_item = GTK_MENU_ITEM (widget);
+      tearoff_item = GTK_TEAROFF_MENU_ITEM (widget);
+
+      gtk_style_set_background (widget->style, widget->window, widget->state);
+      gdk_window_clear_area (widget->window, area->x, area->y, area->width, area->height);
+
+      x = GTK_CONTAINER (menu_item)->border_width;
+      y = GTK_CONTAINER (menu_item)->border_width;
+      width = widget->allocation.width - x * 2;
+      height = widget->allocation.height - y * 2;
+      right_max = x + width;
+
+      if (widget->state == GTK_STATE_PRELIGHT)
+       gtk_draw_shadow (widget->style,
+                        widget->window,
+                        GTK_STATE_PRELIGHT,
+                        GTK_SHADOW_OUT,
+                        x, y, width, height);
+
+      if (tearoff_item->torn_off)
+       {
+         gint arrow_x;
+
+         if (widget->state == GTK_STATE_PRELIGHT)
+           shadow_type = GTK_SHADOW_IN;
+         else
+           shadow_type = GTK_SHADOW_OUT;
+
+         if (menu_item->toggle_size > ARROW_SIZE)
+           {
+             arrow_x = x + (menu_item->toggle_size - ARROW_SIZE)/2;
+             x += menu_item->toggle_size + BORDER_SPACING;
+           }
+         else
+           {
+             arrow_x = ARROW_SIZE / 2;
+             x += 2 * ARROW_SIZE;
+           }
+         
+         gtk_draw_arrow (widget->style, widget->window,
+                         widget->state, shadow_type, GTK_ARROW_LEFT, FALSE,
+                         arrow_x, y + height / 2 - 5, 
+                         ARROW_SIZE, ARROW_SIZE);
+       }
+
+      while (x < right_max)
+       {
+         gtk_draw_hline (widget->style, widget->window, GTK_STATE_NORMAL,
+                         x, MIN (x+TEAR_LENGTH, right_max),
+                         y + (height - widget->style->klass->ythickness)/2);
+         x += 2 * TEAR_LENGTH;
+       }
+    }
+}
+
+static void
+gtk_tearoff_menu_item_draw (GtkWidget    *widget,
+                         GdkRectangle *area)
+{
+  g_return_if_fail (widget != NULL);
+  g_return_if_fail (GTK_IS_TEAROFF_MENU_ITEM (widget));
+  g_return_if_fail (area != NULL);
+
+  gtk_tearoff_menu_item_paint (widget, area);
+}
+
+static gint
+gtk_tearoff_menu_item_expose (GtkWidget      *widget,
+                           GdkEventExpose *event)
+{
+  g_return_val_if_fail (widget != NULL, FALSE);
+  g_return_val_if_fail (GTK_IS_TEAROFF_MENU_ITEM (widget), FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  gtk_tearoff_menu_item_paint (widget, &event->area);
+
+  return FALSE;
+}
+
+static gint
+gtk_tearoff_menu_item_delete_cb (GtkMenuItem *menu_item, GdkEventAny *event)
+{
+  gtk_tearoff_menu_item_activate (menu_item);
+  return TRUE;
+}
+
+static void
+gtk_tearoff_menu_item_activate (GtkMenuItem *menu_item)
+{
+  GtkTearoffMenuItem *tearoff_menu_item;
+
+  g_return_if_fail (menu_item != NULL);
+  g_return_if_fail (GTK_IS_TEAROFF_MENU_ITEM (menu_item));
+
+  tearoff_menu_item = GTK_TEAROFF_MENU_ITEM (menu_item);
+  tearoff_menu_item->torn_off = !tearoff_menu_item->torn_off;
+
+  if (GTK_IS_MENU (GTK_WIDGET (menu_item)->parent))
+    {
+      GtkMenu *menu = GTK_MENU (GTK_WIDGET (menu_item)->parent);
+      gboolean need_connect;
+      
+       need_connect = (tearoff_menu_item->torn_off && !menu->tearoff_window);
+
+       gtk_menu_set_tearoff_state (GTK_MENU (GTK_WIDGET (menu_item)->parent),
+                                   tearoff_menu_item->torn_off);
+
+       if (need_connect)
+         gtk_signal_connect_object (GTK_OBJECT (menu->tearoff_window),  
+                                    "delete_event",
+                                    GTK_SIGNAL_FUNC (gtk_tearoff_menu_item_delete_cb),
+                                    GTK_OBJECT (menu_item));
+    }
+  
+  gtk_widget_queue_resize (GTK_WIDGET (menu_item));
+}
+
diff --git a/gtk/gtktearoffmenuitem.h b/gtk/gtktearoffmenuitem.h
new file mode 100644 (file)
index 0000000..614d361
--- /dev/null
@@ -0,0 +1,64 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __GTK_MENU_TEAROFF_ITEM_H__
+#define __GTK_MENU_TEAROFF_ITEM_H__
+
+
+#include <gdk/gdk.h>
+#include <gtk/gtkmenuitem.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+
+#define GTK_TYPE_TEAROFF_MENU_ITEM           (gtk_tearoff_menu_item_get_type ())
+#define GTK_TEAROFF_MENU_ITEM(obj)           (GTK_CHECK_CAST ((obj), GTK_TYPE_TEAROFF_MENU_ITEM, GtkTearoffMenuItem))
+#define GTK_TEAROFF_MENU_ITEM_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TEAROFF_MENU_ITEM, GtkTearoffMenuItemClass))
+#define GTK_IS_TEAROFF_MENU_ITEM(obj)        (GTK_CHECK_TYPE ((obj), GTK_TYPE_TEAROFF_MENU_ITEM))
+#define GTK_IS_TEAROFF_MENU_ITEM_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TEAROFF_MENU_ITEM))
+
+
+typedef struct _GtkTearoffMenuItem       GtkTearoffMenuItem;
+typedef struct _GtkTearoffMenuItemClass  GtkTearoffMenuItemClass;
+
+struct _GtkTearoffMenuItem
+{
+  GtkMenuItem menu_item;
+
+  guint torn_off : 1;
+};
+
+struct _GtkTearoffMenuItemClass
+{
+  GtkMenuItemClass parent_class;
+};
+
+
+GtkType           gtk_tearoff_menu_item_get_type     (void);
+GtkWidget* gtk_tearoff_menu_item_new         (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_TEAROFF_MENU_ITEM_H__ */
index c38ca05fea48f242e4bc40799221e6f02b43ad88..6e62117ad3e448dc7c015392b014532623ba2dee 100644 (file)
@@ -775,6 +775,8 @@ gtk_toolbar_insert_element (GtkToolbar          *toolbar,
          gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (child->widget), FALSE);
        }
 
+      GTK_WIDGET_UNSET_FLAGS (child->widget, GTK_CAN_FOCUS);
+
       if (callback)
        gtk_signal_connect (GTK_OBJECT (child->widget), "clicked",
                            callback, user_data);
index 156a4aa5f3ac62faa69bdd66b90cf1e5b150dcf6..ca787940c212d00ad8696e7482d2141d003e0913 100644 (file)
@@ -13,6 +13,7 @@ extern GtkType GTK_TYPE_CURVE_TYPE;
 extern GtkType GTK_TYPE_DIRECTION_TYPE;
 extern GtkType GTK_TYPE_JUSTIFICATION;
 extern GtkType GTK_TYPE_MATCH_TYPE;
+extern GtkType GTK_TYPE_MENU_DIRECTION_TYPE;
 extern GtkType GTK_TYPE_MENU_FACTORY_TYPE;
 extern GtkType GTK_TYPE_METRIC_TYPE;
 extern GtkType GTK_TYPE_ORIENTATION;
index ba0eee19d56243ab25894ed0c02c26a1dd2f8e6c..53d010f89701fa66c7fc4dfff0314660300a651f 100644 (file)
@@ -95,6 +95,13 @@ static GtkEnumValue _gtk_match_type_values[] = {
   { GTK_MATCH_LAST, "GTK_MATCH_LAST", "last" },
   { 0, NULL, NULL }
 };
+static GtkEnumValue _gtk_menu_direction_type_values[] = {
+  { GTK_MENU_DIR_PARENT, "GTK_MENU_DIR_PARENT", "parent" },
+  { GTK_MENU_DIR_CHILD, "GTK_MENU_DIR_CHILD", "child" },
+  { GTK_MENU_DIR_NEXT, "GTK_MENU_DIR_NEXT", "next" },
+  { GTK_MENU_DIR_PREV, "GTK_MENU_DIR_PREV", "prev" },
+  { 0, NULL, NULL }
+};
 static GtkEnumValue _gtk_menu_factory_type_values[] = {
   { GTK_MENU_FACTORY_MENU, "GTK_MENU_FACTORY_MENU", "menu" },
   { GTK_MENU_FACTORY_MENU_BAR, "GTK_MENU_FACTORY_MENU_BAR", "menu-bar" },
index feb499a9ac1813676bb285bc8957f17929decc86..f348794a930bd3e20e981acd81e74c21e17b56d0 100644 (file)
@@ -26,6 +26,8 @@
     GTK_TYPE_ENUM, _gtk_justification_values },
   { "GtkMatchType", &GTK_TYPE_MATCH_TYPE,
     GTK_TYPE_ENUM, _gtk_match_type_values },
+  { "GtkMenuDirectionType", &GTK_TYPE_MENU_DIRECTION_TYPE,
+    GTK_TYPE_ENUM, _gtk_menu_direction_type_values },
   { "GtkMenuFactoryType", &GTK_TYPE_MENU_FACTORY_TYPE,
     GTK_TYPE_ENUM, _gtk_menu_factory_type_values },
   { "GtkMetricType", &GTK_TYPE_METRIC_TYPE,
index e0ae2a8a49af5ce065e1da7a3ed55a822673ddce..921dfeb271b308e370b4b6c3e2e19a661fe20fb8 100644 (file)
@@ -13,6 +13,7 @@ GtkType GTK_TYPE_CURVE_TYPE = 0;
 GtkType GTK_TYPE_DIRECTION_TYPE = 0;
 GtkType GTK_TYPE_JUSTIFICATION = 0;
 GtkType GTK_TYPE_MATCH_TYPE = 0;
+GtkType GTK_TYPE_MENU_DIRECTION_TYPE = 0;
 GtkType GTK_TYPE_MENU_FACTORY_TYPE = 0;
 GtkType GTK_TYPE_METRIC_TYPE = 0;
 GtkType GTK_TYPE_ORIENTATION = 0;
index 64796399595daaee31539f20cf88be07727fc766..f22179a5a7fc8a16670a1d9caefff1484618a7cb 100644 (file)
@@ -113,7 +113,7 @@ typedef     struct  _GtkStateData    GtkStateData;
 struct _GtkStateData
 {
   GtkStateType  state;
-  guint                state_restauration : 1;
+  guint                state_restoration : 1;
   guint         parent_sensitive : 1;
 };
 
@@ -2479,7 +2479,7 @@ gtk_widget_set_state (GtkWidget           *widget,
       GtkStateData data;
 
       data.state = state;
-      data.state_restauration = FALSE;
+      data.state_restoration = FALSE;
       if (widget->parent)
        data.parent_sensitive = (GTK_WIDGET_IS_SENSITIVE (widget->parent) != FALSE);
       else
@@ -2524,7 +2524,7 @@ gtk_widget_set_sensitive (GtkWidget *widget,
       GTK_WIDGET_UNSET_FLAGS (widget, GTK_SENSITIVE);
       data.state = GTK_WIDGET_STATE (widget);
     }
-  data.state_restauration = TRUE;
+  data.state_restoration = TRUE;
 
   if (widget->parent)
     data.parent_sensitive = (GTK_WIDGET_IS_SENSITIVE (widget->parent) != FALSE);
@@ -2566,7 +2566,7 @@ gtk_widget_set_parent (GtkWidget *widget,
     data.state = GTK_WIDGET_STATE (parent);
   else
     data.state = GTK_WIDGET_STATE (widget);
-  data.state_restauration = FALSE;
+  data.state_restoration = FALSE;
   data.parent_sensitive = (GTK_WIDGET_IS_SENSITIVE (parent) != FALSE);
 
   gtk_widget_propagate_state (widget, &data);
@@ -3767,7 +3767,7 @@ gtk_widget_propagate_state (GtkWidget           *widget,
 
       if (GTK_WIDGET_IS_SENSITIVE (widget))
        {
-         if (data->state_restauration)
+         if (data->state_restoration)
            GTK_WIDGET_STATE (widget) = GTK_WIDGET_SAVED_STATE (widget);
          else
            GTK_WIDGET_STATE (widget) = data->state;
@@ -3775,7 +3775,7 @@ gtk_widget_propagate_state (GtkWidget           *widget,
       else
        {
          GTK_WIDGET_STATE (widget) = GTK_STATE_INSENSITIVE;
-         if (!data->state_restauration &&
+         if (!data->state_restoration &&
              data->state != GTK_STATE_INSENSITIVE)
            GTK_WIDGET_SAVED_STATE (widget) = data->state;
        }
@@ -3783,7 +3783,7 @@ gtk_widget_propagate_state (GtkWidget           *widget,
   else
     {
       GTK_WIDGET_UNSET_FLAGS (widget, GTK_PARENT_SENSITIVE);
-      if (!data->state_restauration)
+      if (!data->state_restoration)
        {
          if (data->state != GTK_STATE_INSENSITIVE)
            GTK_WIDGET_SAVED_STATE (widget) = data->state;
index dec2fa2d9b2d0eab8debbb02cfef897b24e387eb..6962c27a29345bfa0bc77e700a7588177574a037 100644 (file)
@@ -1949,7 +1949,7 @@ create_tooltips (void)
  */
 
 static GtkWidget*
-create_menu (int depth)
+create_menu (gint depth, gboolean tearoff)
 {
   GtkWidget *menu;
   GtkWidget *menuitem;
@@ -1963,6 +1963,13 @@ create_menu (int depth)
   menu = gtk_menu_new ();
   group = NULL;
 
+  if (tearoff)
+    {
+      menuitem = gtk_tearoff_menu_item_new ();
+      gtk_menu_append (GTK_MENU (menu), menuitem);
+      gtk_widget_show (menuitem);
+    }
+
   for (i = 0, j = 1; i < 5; i++, j++)
     {
       sprintf (buf, "item %2d - %d", depth, j);
@@ -1975,7 +1982,7 @@ create_menu (int depth)
       if (i == 3)
        gtk_widget_set_sensitive (menuitem, FALSE);
 
-      gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (depth - 1));
+      gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (depth - 1, TRUE));
     }
 
   return menu;
@@ -2007,6 +2014,9 @@ create_menus (void)
                          GTK_SIGNAL_FUNC (gtk_true),
                          NULL);
       
+      accel_group = gtk_accel_group_new ();
+      gtk_accel_group_attach (accel_group, GTK_OBJECT (window));
+
       gtk_window_set_title (GTK_WINDOW (window), "menus");
       gtk_container_border_width (GTK_CONTAINER (window), 0);
       
@@ -2019,7 +2029,7 @@ create_menus (void)
       gtk_box_pack_start (GTK_BOX (box1), menubar, FALSE, TRUE, 0);
       gtk_widget_show (menubar);
       
-      menu = create_menu (2);
+      menu = create_menu (2, TRUE);
       
       menuitem = gtk_menu_item_new_with_label ("test\nline2");
       gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), menu);
@@ -2027,12 +2037,12 @@ create_menus (void)
       gtk_widget_show (menuitem);
       
       menuitem = gtk_menu_item_new_with_label ("foo");
-      gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (3));
+      gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (3, TRUE));
       gtk_menu_bar_append (GTK_MENU_BAR (menubar), menuitem);
       gtk_widget_show (menuitem);
-      
+
       menuitem = gtk_menu_item_new_with_label ("bar");
-      gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (4));
+      gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (4, TRUE));
       gtk_menu_item_right_justify (GTK_MENU_ITEM (menuitem));
       gtk_menu_bar_append (GTK_MENU_BAR (menubar), menuitem);
       gtk_widget_show (menuitem);
@@ -2042,8 +2052,7 @@ create_menus (void)
       gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
       gtk_widget_show (box2);
       
-      menu = create_menu (1);
-      accel_group = gtk_accel_group_get_default ();
+      menu = create_menu (1, FALSE);
       gtk_menu_set_accel_group (GTK_MENU (menu), accel_group);
 
       menuitem = gtk_check_menu_item_new_with_label ("Accelerate Me");
@@ -2112,6 +2121,99 @@ create_menus (void)
     gtk_widget_destroy (window);
 }
 
+static GtkItemFactoryEntry menu_items[] =
+{
+  { "/_File",            NULL,         NULL,       0, "<Branch>" },
+  { "/File/tearoff1",    NULL,         NULL,       0, "<Tearoff>" },
+  { "/File/_New",        "<control>N", NULL,       0 },
+  { "/File/_Open",       "<control>O", NULL,       0 },
+  { "/File/_Save",       "<control>S", NULL,       0 },
+  { "/File/Save _As...", NULL,         NULL,       0 },
+  { "/File/sep1",        NULL,         NULL,       0, "<Separator>" },
+  { "/File/_Quit",       "<control>Q", NULL,       0 },
+
+  { "/_Preferences",     NULL,         NULL,       0, "<Branch>" },
+  { "/_Preferences/_Color", NULL,         NULL,       0, "<Branch>" },
+  { "/_Preferences/Color/_Red",      NULL,         NULL,          0, "<RadioItem>" },
+  { "/_Preferences/Color/_Green",   NULL,         NULL,           0, "<RadioItem>" },
+  { "/_Preferences/Color/_Blue",        NULL,         NULL,       0, "<RadioItem>" },
+  { "/_Preferences/_Shape", NULL,         NULL,       0, "<Branch>" },
+  { "/_Preferences/Shape/_Square",      NULL,         NULL,       0, "<RadioItem>" },
+  { "/_Preferences/Shape/_Rectangle",   NULL,         NULL,       0, "<RadioItem>" },
+  { "/_Preferences/Shape/_Oval",        NULL,         NULL,       0, "<RadioItem>" },
+
+  { "/_Help",            NULL,         NULL,       0, "<LastBranch>" },
+  { "/Help/_About",      NULL,         NULL,       0 },
+};
+
+static int nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
+
+static void
+create_item_factory (void)
+{
+  static GtkWidget *window = NULL;
+  
+  if (!window)
+    {
+      GtkWidget *box1;
+      GtkWidget *box2;
+      GtkWidget *separator;
+      GtkWidget *label;
+      GtkWidget *button;
+      GtkAccelGroup *accel_group;
+      GtkItemFactory *item_factory;
+      
+      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+      
+      gtk_signal_connect (GTK_OBJECT (window), "destroy",
+                         GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+                         &window);
+      gtk_signal_connect (GTK_OBJECT (window), "delete-event",
+                         GTK_SIGNAL_FUNC (gtk_true),
+                         NULL);
+      
+      accel_group = gtk_accel_group_new ();
+      item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", accel_group);
+      gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL);
+      gtk_accel_group_attach (accel_group, GTK_OBJECT (window));
+      gtk_window_set_title (GTK_WINDOW (window), "Item Factory");
+      gtk_container_border_width (GTK_CONTAINER (window), 0);
+      
+      box1 = gtk_vbox_new (FALSE, 0);
+      gtk_container_add (GTK_CONTAINER (window), box1);
+      
+      gtk_box_pack_start (GTK_BOX (box1),
+                         gtk_item_factory_get_widget (item_factory, "<main>"),
+                         FALSE, FALSE, 0);
+
+      label = gtk_label_new ("Type\n<alt>\nto start");
+      gtk_widget_set_usize (label, 200, 200);
+      gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
+      gtk_box_pack_start (GTK_BOX (box1), label, TRUE, TRUE, 0);
+
+
+      separator = gtk_hseparator_new ();
+      gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
+
+
+      box2 = gtk_vbox_new (FALSE, 10);
+      gtk_container_border_width (GTK_CONTAINER (box2), 10);
+      gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
+
+      button = gtk_button_new_with_label ("close");
+      gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+                                GTK_SIGNAL_FUNC(gtk_widget_destroy),
+                                GTK_OBJECT (window));
+      gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
+      GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+      gtk_widget_grab_default (button);
+
+      gtk_widget_show_all (window);
+    }
+  else
+    gtk_widget_destroy (window);
+}
+
 /*
  * GtkScrolledWindow
  */
@@ -3145,6 +3247,7 @@ create_list (void)
       separator = gtk_hseparator_new ();
       gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
 
+
       box2 = gtk_vbox_new (FALSE, 10);
       gtk_container_border_width (GTK_CONTAINER (box2), 10);
       gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
@@ -7291,6 +7394,7 @@ create_main_window (void)
       { "font selection", create_font_selection },
       { "gamma curve", create_gamma_curve },
       { "handle box", create_handle_box },
+      { "item factory", create_item_factory },
       { "list", create_list },
       { "menus", create_menus },
       { "modal window", create_modal_window },
index dec2fa2d9b2d0eab8debbb02cfef897b24e387eb..6962c27a29345bfa0bc77e700a7588177574a037 100644 (file)
@@ -1949,7 +1949,7 @@ create_tooltips (void)
  */
 
 static GtkWidget*
-create_menu (int depth)
+create_menu (gint depth, gboolean tearoff)
 {
   GtkWidget *menu;
   GtkWidget *menuitem;
@@ -1963,6 +1963,13 @@ create_menu (int depth)
   menu = gtk_menu_new ();
   group = NULL;
 
+  if (tearoff)
+    {
+      menuitem = gtk_tearoff_menu_item_new ();
+      gtk_menu_append (GTK_MENU (menu), menuitem);
+      gtk_widget_show (menuitem);
+    }
+
   for (i = 0, j = 1; i < 5; i++, j++)
     {
       sprintf (buf, "item %2d - %d", depth, j);
@@ -1975,7 +1982,7 @@ create_menu (int depth)
       if (i == 3)
        gtk_widget_set_sensitive (menuitem, FALSE);
 
-      gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (depth - 1));
+      gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (depth - 1, TRUE));
     }
 
   return menu;
@@ -2007,6 +2014,9 @@ create_menus (void)
                          GTK_SIGNAL_FUNC (gtk_true),
                          NULL);
       
+      accel_group = gtk_accel_group_new ();
+      gtk_accel_group_attach (accel_group, GTK_OBJECT (window));
+
       gtk_window_set_title (GTK_WINDOW (window), "menus");
       gtk_container_border_width (GTK_CONTAINER (window), 0);
       
@@ -2019,7 +2029,7 @@ create_menus (void)
       gtk_box_pack_start (GTK_BOX (box1), menubar, FALSE, TRUE, 0);
       gtk_widget_show (menubar);
       
-      menu = create_menu (2);
+      menu = create_menu (2, TRUE);
       
       menuitem = gtk_menu_item_new_with_label ("test\nline2");
       gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), menu);
@@ -2027,12 +2037,12 @@ create_menus (void)
       gtk_widget_show (menuitem);
       
       menuitem = gtk_menu_item_new_with_label ("foo");
-      gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (3));
+      gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (3, TRUE));
       gtk_menu_bar_append (GTK_MENU_BAR (menubar), menuitem);
       gtk_widget_show (menuitem);
-      
+
       menuitem = gtk_menu_item_new_with_label ("bar");
-      gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (4));
+      gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), create_menu (4, TRUE));
       gtk_menu_item_right_justify (GTK_MENU_ITEM (menuitem));
       gtk_menu_bar_append (GTK_MENU_BAR (menubar), menuitem);
       gtk_widget_show (menuitem);
@@ -2042,8 +2052,7 @@ create_menus (void)
       gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
       gtk_widget_show (box2);
       
-      menu = create_menu (1);
-      accel_group = gtk_accel_group_get_default ();
+      menu = create_menu (1, FALSE);
       gtk_menu_set_accel_group (GTK_MENU (menu), accel_group);
 
       menuitem = gtk_check_menu_item_new_with_label ("Accelerate Me");
@@ -2112,6 +2121,99 @@ create_menus (void)
     gtk_widget_destroy (window);
 }
 
+static GtkItemFactoryEntry menu_items[] =
+{
+  { "/_File",            NULL,         NULL,       0, "<Branch>" },
+  { "/File/tearoff1",    NULL,         NULL,       0, "<Tearoff>" },
+  { "/File/_New",        "<control>N", NULL,       0 },
+  { "/File/_Open",       "<control>O", NULL,       0 },
+  { "/File/_Save",       "<control>S", NULL,       0 },
+  { "/File/Save _As...", NULL,         NULL,       0 },
+  { "/File/sep1",        NULL,         NULL,       0, "<Separator>" },
+  { "/File/_Quit",       "<control>Q", NULL,       0 },
+
+  { "/_Preferences",     NULL,         NULL,       0, "<Branch>" },
+  { "/_Preferences/_Color", NULL,         NULL,       0, "<Branch>" },
+  { "/_Preferences/Color/_Red",      NULL,         NULL,          0, "<RadioItem>" },
+  { "/_Preferences/Color/_Green",   NULL,         NULL,           0, "<RadioItem>" },
+  { "/_Preferences/Color/_Blue",        NULL,         NULL,       0, "<RadioItem>" },
+  { "/_Preferences/_Shape", NULL,         NULL,       0, "<Branch>" },
+  { "/_Preferences/Shape/_Square",      NULL,         NULL,       0, "<RadioItem>" },
+  { "/_Preferences/Shape/_Rectangle",   NULL,         NULL,       0, "<RadioItem>" },
+  { "/_Preferences/Shape/_Oval",        NULL,         NULL,       0, "<RadioItem>" },
+
+  { "/_Help",            NULL,         NULL,       0, "<LastBranch>" },
+  { "/Help/_About",      NULL,         NULL,       0 },
+};
+
+static int nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
+
+static void
+create_item_factory (void)
+{
+  static GtkWidget *window = NULL;
+  
+  if (!window)
+    {
+      GtkWidget *box1;
+      GtkWidget *box2;
+      GtkWidget *separator;
+      GtkWidget *label;
+      GtkWidget *button;
+      GtkAccelGroup *accel_group;
+      GtkItemFactory *item_factory;
+      
+      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+      
+      gtk_signal_connect (GTK_OBJECT (window), "destroy",
+                         GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+                         &window);
+      gtk_signal_connect (GTK_OBJECT (window), "delete-event",
+                         GTK_SIGNAL_FUNC (gtk_true),
+                         NULL);
+      
+      accel_group = gtk_accel_group_new ();
+      item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", accel_group);
+      gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL);
+      gtk_accel_group_attach (accel_group, GTK_OBJECT (window));
+      gtk_window_set_title (GTK_WINDOW (window), "Item Factory");
+      gtk_container_border_width (GTK_CONTAINER (window), 0);
+      
+      box1 = gtk_vbox_new (FALSE, 0);
+      gtk_container_add (GTK_CONTAINER (window), box1);
+      
+      gtk_box_pack_start (GTK_BOX (box1),
+                         gtk_item_factory_get_widget (item_factory, "<main>"),
+                         FALSE, FALSE, 0);
+
+      label = gtk_label_new ("Type\n<alt>\nto start");
+      gtk_widget_set_usize (label, 200, 200);
+      gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
+      gtk_box_pack_start (GTK_BOX (box1), label, TRUE, TRUE, 0);
+
+
+      separator = gtk_hseparator_new ();
+      gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
+
+
+      box2 = gtk_vbox_new (FALSE, 10);
+      gtk_container_border_width (GTK_CONTAINER (box2), 10);
+      gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
+
+      button = gtk_button_new_with_label ("close");
+      gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+                                GTK_SIGNAL_FUNC(gtk_widget_destroy),
+                                GTK_OBJECT (window));
+      gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
+      GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+      gtk_widget_grab_default (button);
+
+      gtk_widget_show_all (window);
+    }
+  else
+    gtk_widget_destroy (window);
+}
+
 /*
  * GtkScrolledWindow
  */
@@ -3145,6 +3247,7 @@ create_list (void)
       separator = gtk_hseparator_new ();
       gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
 
+
       box2 = gtk_vbox_new (FALSE, 10);
       gtk_container_border_width (GTK_CONTAINER (box2), 10);
       gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
@@ -7291,6 +7394,7 @@ create_main_window (void)
       { "font selection", create_font_selection },
       { "gamma curve", create_gamma_curve },
       { "handle box", create_handle_box },
+      { "item factory", create_item_factory },
       { "list", create_list },
       { "menus", create_menus },
       { "modal window", create_modal_window },